Darktrace Deprecated

Deprecated. Use DarktraceMBs, DarktraceAIA, DarktraceAdmin instead.

Network Security · Darktrace

Details

IDDarktrace
ProviderThoma Bravo
CategoryNetwork Security
From Version6.0.0
Docker Imagedemisto/python3:3.10.10.48392
Supported ModulesAgentix XSIAM

README

Darktrace is a Cyber AI platform for threat detection and response across cloud, email, industrial, and the network.
This integration was integrated and tested with version 4.1.0 of Darktrace

Configure Darktrace in Cortex

Parameter Description Required
url Server URL (e.g. https://example.net) True
isFetch Fetch incidents False
insecure Trust any certificate (not secure) False
proxy Use system proxy settings False
public_api_token Public API Token True
private_api_token Private API Token True
min_score Minimum Score True
max_alerts Maximum Model Breaches per Fetch False
first_fetch First fetch time 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.

darktrace-get-breach


Darktrace-get-breach returns a model breach based on its model breach id (pbid)

Base Command

darktrace-get-breach

Input

Argument Name Description Required
pbid Model breach ID Required

Context Output

Path Type Description
Darktrace.ModelBreach.pbid Number Model breach ID
Darktrace.ModelBreach.time Date Model breach generated time.
Darktrace.ModelBreach.commentCount Number Number of comments on the model breach
Darktrace.ModelBreach.score Number Score of Darktrace model breach (0 to 1)
Darktrace.ModelBreach.device.did Number Darktrace device ID of Device that breached the model
Darktrace.ModelBreach.device.macaddress String MAC address of the device involved in the model breach (if applicable)
Darktrace.ModelBreach.device.vendor String Vendor of the device involved in the model breach (if applicable)
Darktrace.ModelBreach.device.ip String IP of the device involved in the model breach (if applicable)
Darktrace.ModelBreach.device.hostname String Hostname of the device involved in the model breach (if applicable)
Darktrace.ModelBreach.device.devicelabel String Device label of the device involved in the model breach (if applicable)
Darktrace.ModelBreach.model.name String Darktrace model that was breached
Darktrace.ModelBreach.model.pid Number Model ID of the model that was breached
Darktrace.ModelBreach.model.uuid String Model UUID of the model that was breached
Darktrace.ModelBreach.model.tags Unknown List of model tags for the model that was breached
Darktrace.ModelBreach.model.priority Number Priority of the model that was breached (0 to 5)
Darktrace.ModelBreach.model.description String Darktrace model description

Command Example

!darktrace-get-breach pbid=95

Context Example

{
    "Darktrace": {
        "ModelBreach": {
            "commentCount": 0,
            "device": {
                "devicelabel": "Kelly's Laptop",
                "did": 823,
                "hostname": "sf-l-kjohnson",
                "ip": "172.31.32.146",
                "macaddress": "06:42:04:c2:b0:48",
                "vendor": "HP"
            },
            "model": {
                "description": "A device is connecting to watched domains or IP addresses. The watch list can be edited from the main GUI menu, Intel sub-menu, under the icon Watched Domains.\\n\\nAction: Review the domain and IP being connected to.",
                "name": "Compromise::Watched Domain",
                "pid": 762,
                "priority": 5,
                "tags": ["AP: C2 Comms"],
                "uuid": "3338210a-8979-4a1b-8039-63ca8addf166"
            },
            "pbid": 95,
            "score": 1,
            "time": "2020-10-08T21:11:21.000Z"
        }
    }
}

Human Readable Output

Darktrace Model Breach 95

commentCount device model pbid score time
0 did: 823
macaddress: 0a:df:4b:52:64:7a
vendor: HP
ip: 172.31.32.146
hostname: ip-172-31-32-146
devicelabel: Kelly’s Laptop
name: Compromise::Watched Domain
pid: 762
uuid: 3338210a-8979-4a1b-8039-63ca8addf166
tags: [AP: C2 Comms]
priority: 5
description: A device is connecting to watched domains or IP addresses. The watch list can be edited from the main GUI menu, Intel sub-menu, under the icon Watched Domains.
95 1 2020-10-08T21:11:21.000Z

darktrace-get-comments


Returns the comments on a model breach based on its model breach id (pbid)

Base Command

darktrace-get-comments

Input

Argument Name Description Required
pbid Model Breach ID Required

Context Output

Path Type Description
Darktrace.ModelBreach.comments Unknown Array of the comments on the model breach

Command Example

!darktrace-get-comments pbid=46

Context Example

{
    "Darktrace": {
        "ModelBreach": {
            "comments": [
                {
                    "message": "Flag for follow-up",
                    "pbid": 46,
                    "pid": 210,
                    "time": "2020-10-08T21:11:21.000Z",
                    "username": "user.one"
                },
                {
                    "message": "Activity has been remediated",
                    "pbid": 46,
                    "pid": 210,
                    "time": "2020-10-08T23:11:21.000Z",
                    "username": "user.two"
                }
            ]
        }
    }
}

Human Readable Output

Darktrace Model Breach 46 Comments

message pbid pid time username
Flag for follow-up 46 210 2020-10-08T21:11:21.000Z user.one
Activity has been remediated 46 210 2020-10-08T23:11:21.000Z user.two

darktrace-acknowledge


Acknowledge a model breach as specified by Model Breach ID

Base Command

darktrace-acknowledge

Input

Argument Name Description Required
pbid Model Breach ID Required

Context Output

Path Type Description
Darktrace.ModelBreach.acknowledged String Whether the model breach is acknowledged in Darktrace
Darktrace.ModelBreach.pbid Number Model breach ID

Command Example

!darktrace-acknowledge pbid=111

Context Example

{
    "Darktrace": {
        "ModelBreach": {
            "acknowledged": true,
            "pbid": 111
        }
    }
}

Human Readable Output

Model Breach 111 Acknowledged

response
Successfully acknowledged.

darktrace-unacknowledge


Unacknowledges a model breach as specified by Model Breach ID

Base Command

darktrace-unacknowledge

Input

Argument Name Description Required
pbid Darktrace model breach ID Required

Context Output

Path Type Description
Darktrace.ModelBreach.acknowledged String Whether the model breach is acknowledged
Darktrace.ModelBreach.pbid Number Model breach ID

Command Example

!darktrace-unacknowledge pbid=111

Context Example

{
    "Darktrace": {
        "ModelBreach": {
            "acknowledged": false,
            "pbid": 111
        }
    }
}

Human Readable Output

Model Breach 111 Unacknowledged

response
Successfully unacknowledged.

darktrace-get-breach-details


Returns details on a modelbreach

Base Command

darktrace-get-breach-details

Input

Argument Name Description Required
pbid Darktrace model breach ID Required
endtime Endtime of data retrieved Not Required
count The amount of lines returned Not Required
offset The offset of data pulled Not Required

Context Output

Path Type Description
Darktrace.ModelBreach Dictionary Details of the model breach

darktrace-get-model


Returns a model given a UUID

Base Command

darktrace-get-model

Input

Argument Name Description Required
uuid Darktrace model ID Required

Context Output

Path Type Description
Darktrace.Model Dictionary Details of the model

darktrace-get-component


Returns the details of a component given a CID

Base Command

darktrace-get-component

Input

Argument Name Description Required
cid Darktrace components ID Required

Context Output

Path Type Description
Darktrace.Component Dictionary Details of the component

darktrace-list-similar-devices


Returns a list of similar devices to a device specified by Darktrace DID

Base Command

darktrace-list-similar-devices

Input

Argument Name Description Required
did Darktrace Device ID Required
max_results Maximum number of results to return Required

Context Output

Path Type Description
Darktrace.SimilarDevices.did Number Darktrace Device ID of the device with the similar devices.
Darktrace.SimilarDevices.devices Unknown List of similar devices and their available information

Command Example

!darktrace-list-similar-devices did=1 max_results=2

Context Example

{
    "Darktrace": {
        "SimilarDevices": {
            "devices": [
                {
                    "did": 823,
                    "firstSeen": "2020-08-07T00:06:40.000Z",
                    "hostname": "ip-172-31-32-146",
                    "ip": "172.31.32.146",
                    "ips": [
                        {
                            "ip": "172.31.32.146",
                            "sid": 114,
                            "time": "2020-09-14 06:00:00",
                            "timems": 1600063200000
                        }
                    ],
                    "lastSeen": "2020-09-14T06:23:38.000Z",
                    "macaddress": "0a:df:4b:52:64:7a",
                    "score": 99,
                    "sid": 114,
                    "typelabel": "Server",
                    "typename": "server",
                    "vendor": ""
                },
                {
                    "did": 3,
                    "firstSeen": "2020-06-09T19:19:32.000Z",
                    "ip": "172.31.16.1",
                    "ips": [
                        {
                            "ip": "172.31.16.1",
                            "sid": 1,
                            "time": "2020-09-11 18:00:00",
                            "timems": 1599847200000
                        }
                    ],
                    "lastSeen": "2020-09-11T18:58:00.000Z",
                    "score": 100,
                    "sid": 1,
                    "typelabel": "Server",
                    "typename": "server"
                }
            ],
            "did": 1
        }
    }
}

Human Readable Output

List of similar devices to device:1

did firstSeen hostname ip ips lastSeen macaddress score sid typelabel typename vendor
823 2020-08-07T00:06:40.000Z ip-172-31-32-146 172.31.32.146 {‘ip’: ‘172.31.32.146’, ‘timems’: 1600063200000, ‘time’: ‘2020-09-14 06:00:00’, ‘sid’: 114} 2020-09-14T06:23:38.000Z 0a:df:4b:52:64:7a 99 114 Server server  
3 2020-06-09T19:19:32.000Z   172.31.16.1 {‘ip’: ‘172.31.16.1’, ‘timems’: 1599847200000, ‘time’: ‘2020-09-11 18:00:00’, ‘sid’: 1} 2020-09-11T18:58:00.000Z   100 1 Server server  

darktrace-get-external-endpoint-details


Returns details collected by Darktrace about external IP addresses or hostnames.

Base Command

darktrace-get-external-endpoint-details

Input

Argument Name Description Required
endpoint_type Type of endpoint: IP or hostname Required
endpoint_value IP or hostname to look up Required
devices Boolean: Include devices that have recently connected to the endpoint Optional
additional_info Boolean: Return additional info about the devices Optional
score Boolean: Return rarity data for this endpoint Optional

Context Output

Path Type Description
Darktrace.ExternalEndpointDetails Unknown Returned information about the external endpoint

Command Example

!darktrace-get-external-endpoint-details endpoint_type=hostname endpoint_value=cats.com additional_info=true devices=true score=true

Context Example

{
    "Darktrace": {
        "ExternalEndpointDetails": {
            "devices": [],
            "dgascore": 0,
            "firsttime": "2020-08-07T04:47:23.000Z",
            "hostname": "cats.com",
            "ips": [],
            "locations": [],
            "popularity": 0
        }
    }
}

Human Readable Output

Hostname: cats.com details

devices dgascore firsttime hostname ips locations popularity
  0 2020-08-07T04:47:23.000Z cats.com     0

darktrace-get-device-connection-info


Returns the graphable data used in the “Connections Data” view for a specific device that can be accessed from the Threat Visualizer omnisearch in Darktrace. Data returned covers a 4 week period. Parameters are further documented at https://customerportal.darktrace.com/product-guides/main/api-deviceinfo-request. It is recommended to run the command to check the relevant fields in context.

Base Command

darktrace-get-device-connection-info

Input

Argument Name Description Required
did Darktrace Device ID Required
data_type Specify whether to return data for either connections (co), data size out (sizeout) or data size in (sizein). Required
external_domain Restrict external data to a particular domain name. Optional
destination_did Darktrace Device DID of destination device to restrict data to. Optional
show_all_graph_data Return an entry for all time intervals in the graph data, including zero counts. (Not recommended) Optional
num_similar_devices Return data for the primary device and this number of similar devices. Optional
full_device_details Return the full device detail objects for all devices referenced by data in an API response. Use of this parameter will alter the JSON structure of the API response for certain calls. Optional

Context Output

Path Type Description
Darktrace.DeviceConnectionInfo Unknown Graphable data used in the “Connections Data” view for a specific device that can be accessed from the Threat Visualizer omnisearch in Darktrace. Data returned covers a 4 week period. Parameters are further documented at https://customerportal.darktrace.com/product-guides/main/api-deviceinfo-request. It is recommended to run the command to check the relevant fields in context.

Command Example

!darktrace-get-device-connection-info did=1 data_type=co

Context Example

{
    "Darktrace": {
        "DeviceConnectionInfo": {
            "deviceInfo": [
                {
                    "did": 1,
                    "graphData": [
                        {
                            "count": 390,
                            "time": 1598302800000
                        },
                        {
                            "count": 7,
                            "time": 1598306400000
                        },
                        {
                            "count": 94,
                            "time": 1598652000000
                        },
                        {
                            "count": 88,
                            "time": 1598990400000
                        },
                        {
                            "count": 25,
                            "time": 1598994000000
                        },
                        {
                            "count": 16,
                            "time": 1598997600000
                        },
                        {
                            "count": 15,
                            "time": 1599001200000
                        },
                        {
                            "count": 25,
                            "time": 1599004800000
                        },
                        {
                            "count": 13,
                            "time": 1599008400000
                        },
                        {
                            "count": 14,
                            "time": 1599012000000
                        },
                        {
                            "count": 13,
                            "time": 1599015600000
                        },
                        {
                            "count": 14,
                            "time": 1599019200000
                        },
                        {
                            "count": 18,
                            "time": 1599022800000
                        },
                        {
                            "count": 14,
                            "time": 1599026400000
                        },
                        {
                            "count": 13,
                            "time": 1599030000000
                        },
                        {
                            "count": 14,
                            "time": 1599033600000
                        },
                        {
                            "count": 13,
                            "time": 1599037200000
                        },
                        {
                            "count": 19,
                            "time": 1599040800000
                        },
                        {
                            "count": 13,
                            "time": 1599044400000
                        },
                        {
                            "count": 14,
                            "time": 1599048000000
                        },
                        {
                            "count": 624,
                            "time": 1599051600000
                        },
                        {
                            "count": 187,
                            "time": 1599055200000
                        },
                        {
                            "count": 169,
                            "time": 1599663600000
                        },
                        {
                            "count": 363,
                            "time": 1599667200000
                        },
                        {
                            "count": 329,
                            "time": 1599670800000
                        },
                        {
                            "count": 324,
                            "time": 1599674400000
                        },
                        {
                            "count": 332,
                            "time": 1599678000000
                        },
                        {
                            "count": 340,
                            "time": 1599681600000
                        },
                        {
                            "count": 334,
                            "time": 1599685200000
                        },
                        {
                            "count": 328,
                            "time": 1599688800000
                        },
                        {
                            "count": 340,
                            "time": 1599692400000
                        },
                        {
                            "count": 330,
                            "time": 1599696000000
                        },
                        {
                            "count": 332,
                            "time": 1599699600000
                        },
                        {
                            "count": 325,
                            "time": 1599703200000
                        },
                        {
                            "count": 344,
                            "time": 1599706800000
                        },
                        {
                            "count": 328,
                            "time": 1599710400000
                        },
                        {
                            "count": 338,
                            "time": 1599714000000
                        },
                        {
                            "count": 76,
                            "time": 1599750000000
                        },
                        {
                            "count": 336,
                            "time": 1599753600000
                        },
                        {
                            "count": 334,
                            "time": 1599757200000
                        },
                        {
                            "count": 334,
                            "time": 1599760800000
                        },
                        {
                            "count": 329,
                            "time": 1599764400000
                        },
                        {
                            "count": 342,
                            "time": 1599768000000
                        },
                        {
                            "count": 329,
                            "time": 1599771600000
                        },
                        {
                            "count": 336,
                            "time": 1599775200000
                        },
                        {
                            "count": 332,
                            "time": 1599778800000
                        },
                        {
                            "count": 332,
                            "time": 1599782400000
                        },
                        {
                            "count": 329,
                            "time": 1599786000000
                        },
                        {
                            "count": 328,
                            "time": 1599789600000
                        },
                        {
                            "count": 332,
                            "time": 1599793200000
                        },
                        {
                            "count": 341,
                            "time": 1599796800000
                        },
                        {
                            "count": 326,
                            "time": 1599800400000
                        },
                        {
                            "count": 330,
                            "time": 1599804000000
                        },
                        {
                            "count": 332,
                            "time": 1599807600000
                        },
                        {
                            "count": 334,
                            "time": 1599811200000
                        },
                        {
                            "count": 335,
                            "time": 1599814800000
                        },
                        {
                            "count": 333,
                            "time": 1599818400000
                        },
                        {
                            "count": 326,
                            "time": 1599822000000
                        },
                        {
                            "count": 328,
                            "time": 1599825600000
                        },
                        {
                            "count": 333,
                            "time": 1599829200000
                        },
                        {
                            "count": 335,
                            "time": 1599832800000
                        },
                        {
                            "count": 339,
                            "time": 1599836400000
                        },
                        {
                            "count": 351,
                            "time": 1599840000000
                        },
                        {
                            "count": 325,
                            "time": 1599843600000
                        },
                        {
                            "count": 329,
                            "time": 1599847200000
                        },
                        {
                            "count": 328,
                            "time": 1599850800000
                        }
                    ],
                    "info": {
                        "devicesAndPorts": [
                            {
                                "deviceAndPort": {
                                    "device": 2,
                                    "direction": "out",
                                    "port": 53
                                },
                                "size": 24
                            },
                            {
                                "deviceAndPort": {
                                    "device": 0,
                                    "direction": "out",
                                    "port": 53
                                },
                                "size": 19
                            },
                            {
                                "deviceAndPort": {
                                    "device": -5,
                                    "direction": "out",
                                    "port": 80
                                },
                                "size": 12
                            },
                            {
                                "deviceAndPort": {
                                    "device": 0,
                                    "direction": "out",
                                    "port": 123
                                },
                                "size": 11
                            },
                            {
                                "deviceAndPort": {
                                    "device": -3,
                                    "direction": "out",
                                    "port": "5001 - 10000"
                                },
                                "size": 10
                            },
                            {
                                "deviceAndPort": {
                                    "device": 3,
                                    "direction": "out",
                                    "port": 67
                                },
                                "size": 9
                            },
                            {
                                "deviceAndPort": {
                                    "device": 0,
                                    "direction": "out",
                                    "port": 443
                                },
                                "size": 4
                            },
                            {
                                "deviceAndPort": {
                                    "device": -6,
                                    "direction": "out",
                                    "port": 1514
                                },
                                "size": 4
                            },
                            {
                                "deviceAndPort": {
                                    "device": 0,
                                    "direction": "out",
                                    "port": 80
                                },
                                "size": 3
                            },
                            {
                                "deviceAndPort": {
                                    "device": -4,
                                    "direction": "out",
                                    "port": "5001 - 10000"
                                },
                                "size": 1
                            },
                            {
                                "deviceAndPort": {
                                    "device": -4,
                                    "direction": "out",
                                    "port": 3289
                                },
                                "size": 1
                            },
                            {
                                "deviceAndPort": {
                                    "device": -4,
                                    "direction": "out",
                                    "port": 1124
                                },
                                "size": 1
                            },
                            {
                                "deviceAndPort": "others",
                                "size": 1
                            }
                        ],
                        "devicesServed": [],
                        "devicesUsed": [
                            {
                                "did": 0,
                                "firstTime": 1591729360000,
                                "size": 37
                            },
                            {
                                "did": 2,
                                "firstTime": 1591729360000,
                                "size": 25
                            },
                            {
                                "did": -5,
                                "firstTime": 1591730027000,
                                "size": 12
                            },
                            {
                                "did": -3,
                                "firstTime": 1591729360000,
                                "size": 10
                            },
                            {
                                "did": 3,
                                "firstTime": 1591730311000,
                                "size": 9
                            },
                            {
                                "did": -6,
                                "firstTime": 1591730311000,
                                "size": 4
                            },
                            {
                                "did": -4,
                                "firstTime": 1591729360000,
                                "size": 2
                            },
                            {
                                "did": "others",
                                "size": 1
                            }
                        ],
                        "portsServed": [],
                        "portsUsed": [
                            {
                                "firstTime": 1591729360000,
                                "port": 53,
                                "size": 44
                            },
                            {
                                "firstTime": 1591729360000,
                                "port": 80,
                                "size": 15
                            },
                            {
                                "firstTime": 1592496475000,
                                "port": "5001 - 10000",
                                "size": 11
                            },
                            {
                                "firstTime": 1591730311000,
                                "port": 123,
                                "size": 11
                            },
                            {
                                "firstTime": 1591730311000,
                                "port": 67,
                                "size": 9
                            },
                            {
                                "firstTime": 1592952598000,
                                "port": 1514,
                                "size": 4
                            },
                            {
                                "firstTime": 1591729361000,
                                "port": 443,
                                "size": 4
                            },
                            {
                                "firstTime": 1592497916000,
                                "port": 3289,
                                "size": 1
                            },
                            {
                                "port": "others",
                                "size": 1
                            }
                        ],
                        "totalDevicesAndPorts": 1589,
                        "totalServed": 0,
                        "totalUsed": 1589
                    },
                    "similarityScore": 100
                }
            ]
        }
    }
}

Human Readable Output

Results for device id: 1

deviceInfo
{‘did’: 1, ‘similarityScore’: 100, ‘graphData’: [{‘time’: 1598302800000, ‘count’: 390}, {‘time’: 1598306400000, ‘count’: 7}, {‘time’: 1598652000000, ‘count’: 94}, {‘time’: 1598990400000, ‘count’: 88}, {‘time’: 1598994000000, ‘count’: 25}, {‘time’: 1598997600000, ‘count’: 16}, {‘time’: 1599001200000, ‘count’: 15}, {‘time’: 1599004800000, ‘count’: 25}, {‘time’: 1599008400000, ‘count’: 13}, {‘time’: 1599012000000, ‘count’: 14}, {‘time’: 1599015600000, ‘count’: 13}, {‘time’: 1599019200000, ‘count’: 14}, {‘time’: 1599022800000, ‘count’: 18}, {‘time’: 1599026400000, ‘count’: 14}, {‘time’: 1599030000000, ‘count’: 13}, {‘time’: 1599033600000, ‘count’: 14}, {‘time’: 1599037200000, ‘count’: 13}, {‘time’: 1599040800000, ‘count’: 19}, {‘time’: 1599044400000, ‘count’: 13}, {‘time’: 1599048000000, ‘count’: 14}, {‘time’: 1599051600000, ‘count’: 624}, {‘time’: 1599055200000, ‘count’: 187}, {‘time’: 1599663600000, ‘count’: 169}, {‘time’: 1599667200000, ‘count’: 363}, {‘time’: 1599670800000, ‘count’: 329}, {‘time’: 1599674400000, ‘count’: 324}, {‘time’: 1599678000000, ‘count’: 332}, {‘time’: 1599681600000, ‘count’: 340}, {‘time’: 1599685200000, ‘count’: 334}, {‘time’: 1599688800000, ‘count’: 328}, {‘time’: 1599692400000, ‘count’: 340}, {‘time’: 1599696000000, ‘count’: 330}, {‘time’: 1599699600000, ‘count’: 332}, {‘time’: 1599703200000, ‘count’: 325}, {‘time’: 1599706800000, ‘count’: 344}, {‘time’: 1599710400000, ‘count’: 328}, {‘time’: 1599714000000, ‘count’: 338}, {‘time’: 1599750000000, ‘count’: 76}, {‘time’: 1599753600000, ‘count’: 336}, {‘time’: 1599757200000, ‘count’: 334}, {‘time’: 1599760800000, ‘count’: 334}, {‘time’: 1599764400000, ‘count’: 329}, {‘time’: 1599768000000, ‘count’: 342}, {‘time’: 1599771600000, ‘count’: 329}, {‘time’: 1599775200000, ‘count’: 336}, {‘time’: 1599778800000, ‘count’: 332}, {‘time’: 1599782400000, ‘count’: 332}, {‘time’: 1599786000000, ‘count’: 329}, {‘time’: 1599789600000, ‘count’: 328}, {‘time’: 1599793200000, ‘count’: 332}, {‘time’: 1599796800000, ‘count’: 341}, {‘time’: 1599800400000, ‘count’: 326}, {‘time’: 1599804000000, ‘count’: 330}, {‘time’: 1599807600000, ‘count’: 332}, {‘time’: 1599811200000, ‘count’: 334}, {‘time’: 1599814800000, ‘count’: 335}, {‘time’: 1599818400000, ‘count’: 333}, {‘time’: 1599822000000, ‘count’: 326}, {‘time’: 1599825600000, ‘count’: 328}, {‘time’: 1599829200000, ‘count’: 333}, {‘time’: 1599832800000, ‘count’: 335}, {‘time’: 1599836400000, ‘count’: 339}, {‘time’: 1599840000000, ‘count’: 351}, {‘time’: 1599843600000, ‘count’: 325}, {‘time’: 1599847200000, ‘count’: 329}, {‘time’: 1599850800000, ‘count’: 328}], ‘info’: {‘totalUsed’: 1589, ‘totalServed’: 0, ‘totalDevicesAndPorts’: 1589, ‘devicesAndPorts’: [{‘deviceAndPort’: {‘direction’: ‘out’, ‘device’: 2, ‘port’: 53}, ‘size’: 24}, {‘deviceAndPort’: {‘direction’: ‘out’, ‘device’: 0, ‘port’: 53}, ‘size’: 19}, {‘deviceAndPort’: {‘direction’: ‘out’, ‘device’: -5, ‘port’: 80}, ‘size’: 12}, {‘deviceAndPort’: {‘direction’: ‘out’, ‘device’: 0, ‘port’: 123}, ‘size’: 11}, {‘deviceAndPort’: {‘direction’: ‘out’, ‘device’: -3, ‘port’: ‘5001 - 10000’}, ‘size’: 10}, {‘deviceAndPort’: {‘direction’: ‘out’, ‘device’: 3, ‘port’: 67}, ‘size’: 9}, {‘deviceAndPort’: {‘direction’: ‘out’, ‘device’: 0, ‘port’: 443}, ‘size’: 4}, {‘deviceAndPort’: {‘direction’: ‘out’, ‘device’: -6, ‘port’: 1514}, ‘size’: 4}, {‘deviceAndPort’: {‘direction’: ‘out’, ‘device’: 0, ‘port’: 80}, ‘size’: 3}, {‘deviceAndPort’: {‘direction’: ‘out’, ‘device’: -4, ‘port’: ‘5001 - 10000’}, ‘size’: 1}, {‘deviceAndPort’: {‘direction’: ‘out’, ‘device’: -4, ‘port’: 3289}, ‘size’: 1}, {‘deviceAndPort’: {‘direction’: ‘out’, ‘device’: -4, ‘port’: 1124}, ‘size’: 1}, {‘deviceAndPort’: ‘others’, ‘size’: 1}], ‘portsUsed’: [{‘port’: 53, ‘size’: 44, ‘firstTime’: 1591729360000}, {‘port’: 80, ‘size’: 15, ‘firstTime’: 1591729360000}, {‘port’: ‘5001 - 10000’, ‘size’: 11, ‘firstTime’: 1592496475000}, {‘port’: 123, ‘size’: 11, ‘firstTime’: 1591730311000}, {‘port’: 67, ‘size’: 9, ‘firstTime’: 1591730311000}, {‘port’: 1514, ‘size’: 4, ‘firstTime’: 1592952598000}, {‘port’: 443, ‘size’: 4, ‘firstTime’: 1591729361000}, {‘port’: 3289, ‘size’: 1, ‘firstTime’: 1592497916000}, {‘port’: ‘others’, ‘size’: 1}], ‘portsServed’: [], ‘devicesUsed’: [{‘did’: 0, ‘size’: 37, ‘firstTime’: 1591729360000}, {‘did’: 2, ‘size’: 25, ‘firstTime’: 1591729360000}, {‘did’: -5, ‘size’: 12, ‘firstTime’: 1591730027000}, {‘did’: -3, ‘size’: 10, ‘firstTime’: 1591729360000}, {‘did’: 3, ‘size’: 9, ‘firstTime’: 1591730311000}, {‘did’: -6, ‘size’: 4, ‘firstTime’: 1591730311000}, {‘did’: -4, ‘size’: 2, ‘firstTime’: 1591729360000}, {‘did’: ‘others’, ‘size’: 1}], ‘devicesServed’: []}}

darktrace-get-device-identity-info


Gets device identity information based on label, tag, type, hostname, ip, mac, vendor and os. It is recommended to run the command to check the relevant fields in context.

Base Command

darktrace-get-device-identity-info

Input

Argument Name Description Required
max_results Max number of devices to return. Default is 50. Optional
order_by Orders the response by the specified filter. Default value is lastSeen. Optional
order Sets the sort order for returned devices as ascending (asc) or descending (desc). Default is ascending. Optional
query A string search. Can query all fields or take a specific field to filter. The query parameter can take a string directly to search all key/value pairs (.e.g query=”value”) or be limited to a certain data type (.e.g query=”label:test”). Fields to filter on are:
- label
- tag
- type
- hostname
- ip
- mac
- vendor
- os
Required

Context Output

Path Type Description
Darktrace.DeviceIdentityInfo Unknown Information about the device’s identity. It is recommended to run the command to check the relevant fields in context.

Command Example

!darktrace-get-device-identity-info query=osSensor

Context Example

{
    "Darktrace": {
        "DeviceIdentityInfo": {
            "devices": [
                {
                    "devicelabel": "Kelly's Laptop",
                    "did": 10,
                    "firstSeen": "2020-06-09T19:02:50.000Z",
                    "hostname": "ip-172-31-17-246",
                    "ip": "172.31.17.246",
                    "ips": [
                        {
                            "ip": "172.31.17.246",
                            "sid": 1,
                            "time": "2020-09-11 19:00:00",
                            "timems": 1599850800000
                        }
                    ],
                    "lastSeen": "2020-09-11T18:22:30.000Z",
                    "macaddress": "06:39:01:c2:b0:48",
                    "sid": 1,
                    "tags": [
                        {
                            "data": {
                                "auto": false,
                                "color": 110,
                                "description": "",
                                "visibility": "Public"
                            },
                            "expiry": 0,
                            "isReferenced": true,
                            "name": "Internet Facing System",
                            "restricted": false,
                            "thid": 54,
                            "tid": 54
                        },
                        {
                            "data": {
                                "auto": false,
                                "color": 181,
                                "description": "",
                                "visibility": "Public"
                            },
                            "expiry": 0,
                            "isReferenced": false,
                            "name": "SF Office",
                            "restricted": false,
                            "thid": 90,
                            "tid": 90
                        }
                    ],
                    "typelabel": "Server",
                    "typename": "server",
                    "vendor": ""
                }
            ],
            "displayedCount": 1,
            "totalCount": 1
        }
    }
}

Human Readable Output

Results for query: osSensor (1 results displayed of 1 which match the query)

devicelabel did firstSeen hostname ip ips lastSeen macaddress sid tags typelabel typename vendor
Kelly’s Laptop 10 2020-06-09T19:02:50.000Z ip-172-31-17-246 172.31.17.246 {‘ip’: ‘172.31.17.246’, ‘timems’: 1599850800000, ‘time’: ‘2020-09-11 19:00:00’, ‘sid’: 1} 2020-09-11T18:22:30.000Z 06:39:01:c2:b0:48 1 {‘tid’: 54, ‘expiry’: 0, ‘thid’: 54, ‘name’: ‘Internet Facing System’, ‘restricted’: False, ‘data’: {‘auto’: False, ‘color’: 110, ‘description’: ‘’, ‘visibility’: ‘Public’}, ‘isReferenced’: True},
{‘tid’: 90, ‘expiry’: 0, ‘thid’: 90, ‘name’: ‘SF Office’, ‘restricted’: False, ‘data’: {‘auto’: False, ‘color’: 181, ‘description’: ‘’, ‘visibility’: ‘Public’}, ‘isReferenced’: False}
Server server  

darktrace-get-entity-details


Returns a time sorted list of connections and events for a device or an entity such as a user credential.

Base Command

darktrace-get-entity-details

Input

Argument Name Description Required
max_results Maximum number of items to return. Default is 50. Optional
offset Starting index to return results from (for example offset=20 with max_results=50 will bring results from index 20 to index 70) Optional
query Comma-separated list of values to filter by.

Examples:
query=”did=1,count=100,eventtype=unusualconnection”
query=”pdid=1,from=2014-12-01T12:00:00,to=2014-12-02T12:00:00”
query=”msg=USER123”

Possible values:
- applicationprotocol
- count
- ddid: Identification number of a destination device modelled in the Darktrace system to restrict data to
- deduplicate: Display only one equivalent connection per hour. (true/false)
- destinationport: This filter can be used to filter the returned data by destination port.
- did: Identification number of a device modelled in the Darktrace system.
- endtime: End time of data to return in millisecond format, relative to midnight January 1st 1970 UTC.
- eventtype: Specifies an type of event to return details for. (connection/unusualconnection/newconnection/notice/devicehistory/modelbreach/userdetails)
- externalhostname: Specifies an hostname to return details for.
- sourceport: This filter can be used to filter the returned data by source port.
- starttime: Start time of data to return in millisecond format, relative to midnight January 1st 1970 UTC.
- to: End time of data to return in YYYY-MM-DD HH:MM:SS format
- uid: Specifies a connection UID to return. (Example: CcdXo43n8B75cdYyI5)
- from: Start time of data to return in YYYY-MM-DD HH:MM:SS format.
- fulldevicedetails: Returns the full device detail objects for all devices referenced by data in an API response.
- intext: This filter can be used to filter the returned data to that which interacts with external sources and destinations, or is restricted to internal. (internal/external)
- msg: Specifies the value of the message field in notice events to return details for. Typically used to specify user credential strings.
- odid: Identification number of a device modelled in the Darktrace system to restrict data to. Typically used with ddid and odid to specify device pairs regardless of source/destination.
- pbid: ID for a model breach
- port: This filter can be used to filter the returned data by source or destination port.
- protocol

For more info on this query visit: https://<your-Darktrace-server-url>/apihelp (‘details’ tab)
Required

Context Output

Path Type Description
Darktrace.EntityDetails Unknown List of entities and their details. Each entity might have different keys. It is recommended to run the command once to check the relevant outputs in context.

Command Example

!darktrace-get-entity-details query=did=1,count=10 offset=5

Context Example

{
    "Darktrace": {
        "EntityDetails": [
            {
                "action": "connection",
                "applicationprotocol": "SSH",
                "ddid": 10,
                "destination": "Kelly's Laptop",
                "destinationDevice": {
                    "devicelabel": "Kelly's Laptop",
                    "did": 10,
                    "hostname": "ip-172-31-17-246",
                    "id": 1,
                    "ip": "172.31.17.246",
                    "ips": [
                        {
                            "ip": "172.31.17.246",
                            "sid": 1,
                            "time": "2020-09-11 19:00:00",
                            "timems": 1599850800000
                        }
                    ],
                    "macaddress": "06:39:01:c2:b0:48",
                    "sid": 1,
                    "time": "1591729370000",
                    "typelabel": "Server",
                    "typename": "server"
                },
                "destinationPort": 22,
                "direction": "in",
                "eventType": "connection",
                "port": 22,
                "protocol": "TCP",
                "source": "222.186.15.62",
                "sourceDevice": {
                    "asn": "AS23650 AS Number for CHINANET jiangsu province backbone",
                    "connectionippopularity": "0",
                    "country": "China",
                    "countrycode": "CN",
                    "ip": "222.186.15.62",
                    "ippopularity": "0",
                    "latitude": 34.772,
                    "longitude": 113.727,
                    "region": "Asia"
                },
                "sourcePort": 17815,
                "status": "ongoing",
                "time": "2020-09-11 19:42:21",
                "timems": 1599853341264,
                "uid": "CJDfGwAT7fVxNJd01"
            },
            {
                "action": "connection",
                "applicationprotocol": "SSH",
                "ddid": 10,
                "destination": "Kelly's Laptop",
                "destinationDevice": {
                    "devicelabel": "Kelly's Laptop",
                    "did": 10,
                    "hostname": "ip-172-31-17-246",
                    "id": 1,
                    "ip": "172.31.17.246",
                    "ips": [
                        {
                            "ip": "172.31.17.246",
                            "sid": 1,
                            "time": "2020-09-11 19:00:00",
                            "timems": 1599850800000
                        }
                    ],
                    "macaddress": "06:39:01:c2:b0:48",
                    "sid": 1,
                    "time": "1591729370000",
                    "typelabel": "Server",
                    "typename": "server"
                },
                "destinationPort": 22,
                "direction": "in",
                "eventType": "connection",
                "port": 22,
                "protocol": "TCP",
                "source": "222.186.15.62",
                "sourceDevice": {
                    "asn": "AS23650 AS Number for CHINANET jiangsu province backbone",
                    "connectionippopularity": "0",
                    "country": "China",
                    "countrycode": "CN",
                    "ip": "222.186.15.62",
                    "ippopularity": "0",
                    "latitude": 34.772,
                    "longitude": 113.727,
                    "region": "Asia"
                },
                "sourcePort": 17815,
                "time": "2020-09-11 19:42:14",
                "timems": 1599853334254,
                "uid": "CJDfGwAT7fVxNJd01"
            },
            {
                "action": "connection",
                "applicationprotocol": "Unknown",
                "ddid": 10,
                "destination": "Kelly's Laptop",
                "destinationDevice": {
                    "devicelabel": "Kelly's Laptop",
                    "did": 10,
                    "hostname": "ip-172-31-17-246",
                    "id": 1,
                    "ip": "172.31.17.246",
                    "ips": [
                        {
                            "ip": "172.31.17.246",
                            "sid": 1,
                            "time": "2020-09-11 19:00:00",
                            "timems": 1599850800000
                        }
                    ],
                    "macaddress": "06:39:01:c2:b0:48",
                    "sid": 1,
                    "time": "1591729370000",
                    "typelabel": "Server",
                    "typename": "server"
                },
                "destinationPort": 443,
                "direction": "in",
                "eventType": "connection",
                "port": 443,
                "protocol": "TCP",
                "source": "62.113.227.26",
                "sourceDevice": {
                    "asn": "AS47447 23media GmbH",
                    "connectionippopularity": "0",
                    "country": "Germany",
                    "countrycode": "DE",
                    "ip": "62.113.227.26",
                    "ippopularity": "0",
                    "latitude": 51.299,
                    "longitude": 9.491,
                    "region": "Europe"
                },
                "sourcePort": 28228,
                "status": "failed",
                "time": "2020-09-11 19:41:23",
                "timems": 1599853283240,
                "uid": "CQ4hu824CoXul9KV01"
            },
            {
                "action": "connection",
                "applicationprotocol": "Unknown",
                "ddid": 10,
                "destination": "Kelly's Laptop",
                "destinationDevice": {
                    "devicelabel": "Kelly's Laptop",
                    "did": 10,
                    "hostname": "ip-172-31-17-246",
                    "id": 1,
                    "ip": "172.31.17.246",
                    "ips": [
                        {
                            "ip": "172.31.17.246",
                            "sid": 1,
                            "time": "2020-09-11 19:00:00",
                            "timems": 1599850800000
                        }
                    ],
                    "macaddress": "06:39:01:c2:b0:48",
                    "sid": 1,
                    "time": "1591729370000",
                    "typelabel": "Server",
                    "typename": "server"
                },
                "destinationPort": 443,
                "direction": "in",
                "eventType": "connection",
                "port": 443,
                "protocol": "TCP",
                "source": "62.113.227.26",
                "sourceDevice": {
                    "asn": "AS47447 23media GmbH",
                    "connectionippopularity": "0",
                    "country": "Germany",
                    "countrycode": "DE",
                    "ip": "62.113.227.26",
                    "ippopularity": "0",
                    "latitude": 51.299,
                    "longitude": 9.491,
                    "region": "Europe"
                },
                "sourcePort": 54518,
                "status": "failed",
                "time": "2020-09-11 19:41:03",
                "timems": 1599853263230,
                "uid": "CWYWpz2KmHrsjNGO01"
            },
            {
                "action": "notice",
                "destination": "Kelly's Laptop",
                "destinationDevice": {
                    "devicelabel": "Kelly's Laptop",
                    "did": 10,
                    "hostname": "ip-172-31-17-246",
                    "id": 1,
                    "ip": "172.31.17.246",
                    "ips": [
                        {
                            "ip": "172.31.17.246",
                            "sid": 1,
                            "time": "2020-09-11 19:00:00",
                            "timems": 1599850800000
                        }
                    ],
                    "macaddress": "06:39:01:c2:b0:48",
                    "sid": 1,
                    "time": "1591729370000",
                    "typelabel": "Server",
                    "typename": "server"
                },
                "destinationPort": 22,
                "details": "2073 bytes delivered in connection and 0 bytes undelivered.",
                "direction": "in",
                "eventType": "notice",
                "mlid": 328,
                "msg": "Unable to determine login failure or success from encrypted traffic.",
                "nid": 35987,
                "source": "13.85.152.27",
                "sourceDevice": {
                    "asn": "AS8075 MICROSOFT-CORP-MSN-AS-BLOCK",
                    "city": "San Antonio",
                    "country": "United States",
                    "countrycode": "US",
                    "ip": "13.85.152.27",
                    "ippopularity": "0",
                    "latitude": 29.422,
                    "longitude": -98.493,
                    "region": "North America"
                },
                "time": "2020-09-11 19:40:48",
                "timems": 1599853248000,
                "type": "SSH::Undetermined_Encryption_Step",
                "uid": "CMEAtvytG16vv0X01"
            }
        ]
    }
}

Human Readable Output

Results

action applicationprotocol ddid destination destinationDevice destinationPort direction eventType port protocol source sourceDevice sourcePort status time timems uid
connection SSH 10 Kelly’s Laptop id: 1
did: 1
macaddress: 06:39:01:c2:b0:48
ip: 172.31.17.246
ips: {‘ip’: ‘172.31.17.246’, ‘timems’: 1599850800000, ‘time’: ‘2020-09-11 19:00:00’, ‘sid’: 1}
sid: 1
hostname: ip-172-31-17-246
time: 1591729370000
devicelabel: Kelly’s Laptop
typename: server
typelabel: Server
22 in connection 22 TCP 222.186.15.62 longitude: 113.727
latitude: 34.772
country: China
countrycode: CN
asn: AS23650 AS Number for CHINANET jiangsu province backbone
region: Asia
ip: 222.186.15.62
ippopularity: 0
connectionippopularity: 0
17815 ongoing 2020-09-11 19:42:21 1599853341264 CJDfGwAT7fVxNJd01
connection SSH 10 Kelly’s Laptop id: 1
did: 1
macaddress: 06:39:01:c2:b0:48
ip: 172.31.17.246
ips: {‘ip’: ‘172.31.17.246’, ‘timems’: 1599850800000, ‘time’: ‘2020-09-11 19:00:00’, ‘sid’: 1}
sid: 1
hostname: ip-172-31-17-246
time: 1591729370000
devicelabel: Kelly’s Laptop
typename: server
typelabel: Server
22 in connection 22 TCP 222.186.15.62 longitude: 113.727
latitude: 34.772
country: China
countrycode: CN
asn: AS23650 AS Number for CHINANET jiangsu province backbone
region: Asia
ip: 222.186.15.62
ippopularity: 0
connectionippopularity: 0
17815   2020-09-11 19:42:14 1599853334254 CJDfGwAT7fVxNJd01
connection Unknown 10 Kelly’s Laptop id: 1
did: 1
macaddress: 06:39:01:c2:b0:48
ip: 172.31.17.246
ips: {‘ip’: ‘172.31.17.246’, ‘timems’: 1599850800000, ‘time’: ‘2020-09-11 19:00:00’, ‘sid’: 1}
sid: 1
hostname: ip-172-31-17-246
time: 1591729370000
devicelabel: Kelly’s Laptop
typename: server
typelabel: Server
443 in connection 443 TCP 62.113.227.26 longitude: 9.491
latitude: 51.299
country: Germany
countrycode: DE
asn: AS47447 23media GmbH
region: Europe
ip: 62.113.227.26
ippopularity: 0
connectionippopularity: 0
28228 failed 2020-09-11 19:41:23 1599853283240 CQ4hu824CoXul9KV01
connection Unknown 10 Kelly’s Laptop id: 1
did: 1
macaddress: 06:39:01:c2:b0:48
ip: 172.31.17.246
ips: {‘ip’: ‘172.31.17.246’, ‘timems’: 1599850800000, ‘time’: ‘2020-09-11 19:00:00’, ‘sid’: 1}
sid: 1
hostname: ip-172-31-17-246
time: 1591729370000
devicelabel: Kelly’s Laptop
typename: server
typelabel: Server
443 in connection 443 TCP 62.113.227.26 longitude: 9.491
latitude: 51.299
country: Germany
countrycode: DE
asn: AS47447 23media GmbH
region: Europe
ip: 62.113.227.26
ippopularity: 0
connectionippopularity: 0
54518 failed 2020-09-11 19:41:03 1599853263230 CWYWpz2KmHrsjNGO01
notice     Kelly’s Laptop id: 1
did: 1
macaddress: 06:39:01:c2:b0:48
ip: 172.31.17.246
ips: {‘ip’: ‘172.31.17.246’, ‘timems’: 1599850800000, ‘time’: ‘2020-09-11 19:00:00’, ‘sid’: 1}
sid: 1
hostname: ip-172-31-17-246
time: 1591729370000
devicelabel: Kelly’s Laptop
typename: server
typelabel: Server
22 in notice     13.85.152.27 longitude: -98.493
latitude: 29.422
city: San Antonio
country: United States
countrycode: US
asn: AS8075 MICROSOFT-CORP-MSN-AS-BLOCK
region: North America
ip: 13.85.152.27
ippopularity: 0
    2020-09-11 19:40:48 1599853248000 CMEAtvytG16vv0X01

Configuration parameters

  • url — Server URL (e.g. https://example.net) (required)
  • isFetch — Fetch incidents
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • incidentType — Incident type
  • public_creds
  • private_creds
  • min_score — Minimum Score (required)
  • max_fetch — Maximum Model Breaches per Fetch
  • first_fetch — First fetch time
  • public_api_token — Public API Token (Deprecated)
  • private_api_token — Private API Token (Deprecated)

Commands (12)

  • darktrace-acknowledge

    Acknowledge a model breach as specified by Model Breach ID

  • darktrace-get-breach

    Darktrace-get-breach returns a model breach based on its model breach id (pbid)

  • darktrace-get-breach-details

    Retrieve additional details on a modelbreach

  • darktrace-get-comments

    Returns the comments on a model breach based on its model breach id (pbid)

  • darktrace-get-component

    Get details of a component given the CID

  • darktrace-get-device-connection-info

    Returns the graphable data used in the "Connections Data" view for a specific device that can be accessed from the Threat Visualizer omnisearch in Darktrace. Data returned covers a 4 week period. Parameters are further documented at https://customerportal.darktrace.com/product-guides/main/api-deviceinfo-request. It is recommended to run the command to check the relevant fields in context.

  • darktrace-get-device-identity-info

    Gets device identity information based on label, tag, type, hostname, ip, mac, vendor and os. It is recommended to run the command to check the relevant fields in context.

  • darktrace-get-entity-details

    Returns a time sorted list of connections and events for a device or an entity such as a user credential.

  • darktrace-get-external-endpoint-details

    Returns details collected by Darktrace about external IP addresses or hostnames.

  • darktrace-get-model

    Get the details of a model given the UUID

  • darktrace-list-similar-devices

    Returns a list of similar devices to a device specified by Darktrace DID

  • darktrace-unacknowledge

    Unacknowledges a model breach as specified by Model Breach ID

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

import json
import urllib3
import dateparser
from datetime import datetime
import traceback
from typing import Any, Dict, Tuple, List, Optional, cast
from copy import copy

import hmac
import hashlib

"""Darktrace Integration for Cortex XSOAR (aka Demisto)"""

# Disable insecure warnings
urllib3.disable_warnings()

"""*****CONSTANTS*****"""

DATE_FORMAT = '%Y-%m-%dT%H:%M:%SZ'
MAX_INCIDENTS_TO_FETCH = 50
MIN_SCORE_TO_FETCH = 0

# For API call mapping
PARAMS_DICTIONARY = {
    'did': 'did',
    'data_type': 'datatype',
    'external_domain': 'externaldomain',
    'full_device_details': 'fulldevicedetails',
    'destination_did': 'oid',
    'show_all_graph_data': 'showallgraphdata',
    'num_similar_devices': 'similardevices',
    'breach_id': 'pbid',
    'host_name': 'hostname',
    'order_by': 'orderBy',
    'max_results': 'count'
}


"""*****CLIENT CLASS*****
Wraps all the code that interacts with the Darktrace API."""


class Client(BaseClient):
    """Client class to interact with the Darktrace API
    This Client implements API calls, and does not contain any Demisto logic.
    Should only do requests and return data.
    It inherits from BaseClient defined in CommonServer Python.
    Most calls use _http_request() that handles proxy, SSL verification, etc.
    """

    def get_modelbreach(self, pbid):
        """Searches for a single Darktrace model breach alerts using '/modelbreaches?pbid=<pbid>'
        :type pbid: ``str``
        :param pbid: Model breach ID of the model breach to get
        :return: list containing the found Darktrace model breach as a Dict
        :rtype: ``List[Dict[str, Any]]``
        """
        request = f"/modelbreaches?pbid={pbid}"
        http_headers = get_headers(self._auth, request)
        return self._http_request(
            method='GET',
            url_suffix=request,
            headers=http_headers
        )

    def get_modelbreach_details(self, pbid, endtime, count, offset):

        request = f"/details?endTime={endtime}&order=desc&includetotalbytes=true&offset={offset}&count={count}&pbid={pbid}"
        http_headers = get_headers(self._auth, request)
        return self._http_request(
            method='GET',
            url_suffix=request,
            headers=http_headers
        )

    def get_model(self, uuid):

        request = "/models?uuid=" + uuid
        http_headers = get_headers(self._auth, request)
        return self._http_request(
            method='GET',
            url_suffix=request,
            headers=http_headers
        )

    def get_component(self, cid):

        request = "/components/" + cid
        http_headers = get_headers(self._auth, request)
        return self._http_request(
            method='GET',
            url_suffix=request,
            headers=http_headers
        )

    def get_modelbreach_comments(self, pbid):
        """Searches for comments on a modelbreach using '/modelbreaches/<pbid>/comments'
        :type pbid: ``str``
        :param pbid: Model breach ID of the model breach to get
        :return: list containing the found Darktrace model breach as a Dict
        :rtype: ``List[Dict[str, Any]]``
        """
        request = "/modelbreaches/" + pbid + "/comments"
        http_headers = get_headers(self._auth, request)
        return self._http_request(
            method='GET',
            url_suffix=request,
            headers=http_headers
        )

    def acknowledge_breach(self, pbid):
        """Acknowledges a modelbreach using '/modelbreaches/<pbid>/acknowledge?acknowledge=true'
        :type pbid: ``str``
        :param pbid: Model breach ID of the model breach to get
        :return: list containing the found Darktrace model breach as a Dict
        :rtype: ``List[Dict[str, Any]]``
        """
        request = "/modelbreaches/" + pbid + "/acknowledge?acknowledge=true"
        http_headers = get_headers(self._auth, request)
        return self._http_request(
            method='POST',
            url_suffix=request,
            headers=http_headers,
            data={"acknowledge": "true"}
        )

    def unacknowledge_breach(self, pbid):
        """Unacknowledges a modelbreach using '/modelbreaches/<pbid>/unacknowledge?unacknowledge=true'
        :type pbid: ``str``
        :param pbid: Model breach ID of the model breach to get
        :return: list containing the found Darktrace model breach as a Dict
        :rtype: ``List[Dict[str, Any]]``
        """
        request = "/modelbreaches/" + pbid + "/unacknowledge?unacknowledge=true"
        http_headers = get_headers(self._auth, request)
        return self._http_request(
            method='POST',
            url_suffix=request,
            headers=http_headers,
            data={"unacknowledge": "true"}
        )

    def list_similar_devices(self, did, max_results):
        """Returns a list of similar devices using '/similardevices'
        :type did: ``str``
        :param did: Device ID of device
        :type max_results: ``str``
        :param max_results: Max # of results to return
        :return: list containing the found Darktrace model breach as a Dict
        :rtype: ``List[Dict[str, Any]]``
        """
        request = "/similardevices?did=" + did + "&count=" + max_results
        http_headers = get_headers(self._auth, request)
        return self._http_request(
            method='GET',
            url_suffix=request,
            headers=http_headers,
        )

    def get_external_endpoint_details(self, endpoint_type, endpoint_value, additional_info, devices, score):
        """Returns information from Darktrace about external endpoints using '/endpointdetails'
        :type endpoint_type: ``str``
        :param endpoint_type: Type of endpoint, IP or hostname
        :type endpoint_value: ``str``
        :param endpoint_value: Value of IP or hostname
        :type additional_info: ``str``
        :param additional_info: Whether to include additional info
        :type devices: ``str``
        :param devices: Whether to include additional devices that connected to the endpoint
        :type score: ``str``
        :param score: Whether to include external endpoint score
        :return: list containing the found Darktrace model breach as a Dict
        :rtype: ``List[Dict[str, Any]]``
        """
        request = f"/endpointdetails?{endpoint_type}={endpoint_value}&additionalinfo={additional_info}" \
                  f"&devices={devices}&score={score}"
        http_headers = get_headers(self._auth, request)
        return self._http_request(
            method='GET',
            url_suffix=request,
            headers=http_headers,
        )

    def get_device_connection_info(self, did, data_type, external_domain, destination_did,
                                   show_all_graph_data, full_device_details, num_similar_devices):
        """Returns information from Darktrace about graphical connection data for devices using '/deviceinfo'
        :type did: ``str``
        :param did: Darktrace Device ID
        :type data_type: ``str``
        :param data_type: Whether to return data for either connections (connections), data size out (sizeout) or
        data size in (sizein)
        :type external_domain: ``str``
        :param external_domain: Whether to restrict external data to a particular domain name.
        :type destination_did: ``str``
        :param destination_did: Darktrace Device DID of destination device to restrict data to.
        :type show_all_graph_data: ``str``
        :param show_all_graph_data: Whether to return an entry for all time intervals
        :type full_device_details: ``str``
        :param full_device_details: Whether to return the full device detail objects
        :type num_similar_devices: ``str``
        :param num_similar_devices: Num similar devices to include
        :return: list containing the connection info as a Dict
        :rtype: ``List[Dict[str, Any]]``
        """
        query_dict = copy(locals())
        query_dict.pop('self')
        query_string = create_query_from_dict(query_dict)
        request = "/deviceinfo" + query_string
        http_headers = get_headers(self._auth, request)
        return self._http_request(
            method='GET',
            url_suffix=request,
            headers=http_headers,
        )

    def get_device_identity_info(self, max_results, order_by, order, query):
        """Returns information from Darktrace about identifying  data for devices using '/devicesearch'
        :type max_results: ``str``
        :param max_results: Darktrace Device ID
        :type order_by: ``str``
        :param order_by: Whether to return data for either connections (connections), data size out (sizeout) or
        data size in (sizein)
        :type order: ``str``
        :param order: Whether to restrict external data to a particular domain name.
        :type query: ``str``
        :param query: Darktrace Device DID of destination device to restrict data to.
        :return: list containing the device info as a Dict
        :rtype: ``List[Dict[str, Any]]``
        """
        query_dict = copy(locals())
        query_dict.pop('self')
        query_string = create_query_from_dict(query_dict)
        request = "/devicesearch" + query_string
        http_headers = get_headers(self._auth, request)
        return self._http_request(
            method='GET',
            url_suffix=request,
            headers=http_headers,
        )

    def get_entity_details(self, max_results, offset, query_list):
        """Returns information from Darktrace about entities using '/details'
        :type max_results: ``int``
        :param max_results: Darktrace Device ID
        :type offset: ``int``
        :param offset: Offset index to start returning queries from.
        :type query: ``list``
        :param query: List-separated query
        :return: list containing the device info as a Dict
        :rtype: ``List[Dict[str, Any]]``
        """
        query_string = create_query_from_list(query_list)
        request = '/details' + query_string
        http_headers = get_headers(self._auth, request)

        res = self._http_request(
            method='GET',
            url_suffix=request,
            headers=http_headers
        )

        if not isinstance(res, list):
            raise Exception(f'Error getting results:\n {res}')
        if offset > len(res):
            raise Exception(f'Offset argument: {offset}, is grater than the amount of results: {len(res)}')
        truncated_response = res[offset:offset + max_results]

        return truncated_response, res

    def search_modelbreaches(self, min_score: float,
                             start_time: Optional[int]) -> List[Dict[str, Any]]:
        """Searches for Darktrace alerts using the '/modelbreaches' API endpoint
        :type min_score: ``float``
        :param min_score: min score of the alert to search for. Range [0, 1].
        :type start_time: ``Optional[int]``
        :param start_time: start timestamp (epoch in seconds) for the alert search
        :return: list containing the found Darktrace model breaches as dicts
        :rtype: ``List[Dict[str, Any]]``
        """
        request = '/modelbreaches'
        request = request + '?minscore=' + str(min_score)
        request = request + '&starttime=' + str(start_time)

        http_headers = get_headers(self._auth, request)

        return self._http_request(
            method='GET',
            url_suffix=request,
            headers=http_headers
        )


"""*****HELPER FUNCTIONS****"""


def arg_to_timestamp(arg: Any, arg_name: str, required: bool = False) -> Optional[int]:
    """Converts an XSOAR argument to a timestamp (seconds from epoch)
    This function is used to quickly validate an argument provided to XSOAR
    via ``demisto.args()`` into an ``int`` containing a timestamp (seconds
    since epoch). It will throw a ValueError if the input is invalid.
    If the input is None, it will throw a ValueError if required is ``True``,
    or ``None`` if required is ``False.
    :type arg: ``Any``
    :param arg: argument to convert
    :type arg_name: ``str``
    :param arg_name: argument name
    :type required: ``bool``
    :param required:
        throws exception if ``True`` and argument provided is None
    :return:
        returns an ``int`` containing a timestamp (seconds from epoch) if conversion works
        returns ``None`` if arg is ``None`` and required is set to ``False``
        otherwise throws an Exception
    :rtype: ``Optional[int]``
    """

    if arg is None:
        if required is True:
            raise ValueError(f'Missing "{arg_name}"')
        return None

    if isinstance(arg, str) and arg.isdigit():
        # timestamp is a str containing digits - we just convert it to int
        return int(arg)
    if isinstance(arg, str):
        # we use dateparser to handle strings either in ISO8601 format, or
        # relative time stamps.
        # For example: format 2019-10-23T00:00:00 or "3 days", etc
        date = dateparser.parse(arg, settings={'TIMEZONE': 'UTC'})
        if date is None:
            # if d is None it means dateparser failed to parse it
            raise ValueError(f'Invalid date: {arg_name}')

        return int(date.timestamp())
    if isinstance(arg, (int, float)):
        # Convert to int if the input is a float
        return int(arg)
    raise ValueError(f'Invalid date: "{arg_name}"')


def arg_to_int(arg: Any, arg_name: str, required: bool = False) -> Optional[int]:
    """Converts an XSOAR argument to a Python int
    This function is used to quickly validate an argument provided to XSOAR
    via ``demisto.args()`` into an ``int`` type. It will throw a ValueError
    if the input is invalid. If the input is None, it will throw a ValueError
    if required is ``True``, or ``None`` if required is ``False.
    :type arg: ``Any``
    :param arg: argument to convert
    :type arg_name: ``str``
    :param arg_name: argument name
    :type required: ``bool``
    :param required:
        throws exception if ``True`` and argument provided is None
    :return:
        returns an ``int`` if arg can be converted
        returns ``None`` if arg is ``None`` and required is set to ``False``
        otherwise throws an Exception
    :rtype: ``Optional[int]``
    """

    if arg is None:
        if required is True:
            raise ValueError(f'Missing "{arg_name}"')
        return None
    if isinstance(arg, str):
        if arg.isdigit():
            return int(arg)
        raise ValueError(f'Invalid number: "{arg_name}"="{arg}"')
    if isinstance(arg, int):
        return arg
    raise ValueError(f'Invalid number: "{arg_name}"')


def get_headers(tokens: tuple, request: str) -> Dict[str, str]:
    """Returns the appropriate HTTP Header for token authentication.
    :type tokens: ``tuple``
    :param tokens: Tuple containing the PUBLIC and PRIVATE API tokens
    :type request: ``str``
    :param request: API request being made, ex: /modelbreaches
    :return:
        returns a Dictionary of the necessary HTTP Headers
    :rtype: ``Dict[str, str]``
    """
    d = datetime.utcnow()
    now = d.strftime('%Y%m%dT%H%M%S')
    public = tokens[0]
    private = tokens[1]
    maccer = hmac.new(private.encode('ASCII'),
                      (request + '\n' + public + '\n' + now).encode('ASCII'), hashlib.sha1)
    sig = maccer.hexdigest()
    headers = {'DTAPI-Token': public, 'DTAPI-Date': now, 'DTAPI-Signature': sig}
    return headers


def create_query_from_dict(param_dict: Dict[str, str]):
    """Returns a query string based on a provided dict.
    :type param_dict: ``Dict[str, str]``
    :param param_dict: Dictionary of parameters
    :return:
        returns the query string
    :rtype: ``str``
    """
    query_string = '?'
    for key, val in param_dict.items():
        if val:
            query_string = query_string + f'{PARAMS_DICTIONARY.get(key, key)}={val}&'
    # remove the last '&' in the string
    return query_string[:-1] if param_dict else ''


def create_query_from_list(param_list: Dict[str, str]):
    """Returns a query string based on a provided list.
    :type param_list: ``List``
    :param param_list: List of parameters
    :return:
        returns the query string
    :rtype: ``str``
    """
    translated_list = [PARAMS_DICTIONARY.get(param, param) for param in param_list]
    query = '?' + '&'.join(translated_list)
    return query if param_list else ''


def format_JSON_for_fetch_incidents(modelbreach: Dict[str, Any]) -> Dict[str, Any]:
    """Formats JSON for fetch incidents.
    :type modelbreach: ``Dict[str, Any]``
    :param modelbreach: JSON model breach as returned by API for fetch incident
    :return: Filtered JSON containing only relevant fields for context
    :rtype: ``Dict[str, Any]``
    """
    relevant_info = {}

    relevant_info['commentCount'] = modelbreach['commentCount'] if 'commentCount' in modelbreach else 'No comment count'
    relevant_info['pbid'] = modelbreach['pbid'] if 'pbid' in modelbreach else 'No Darktrace pbid'
    relevant_info['time'] = modelbreach['time'] if 'time' in modelbreach else 'No Darktrace model breach time'
    relevant_info['score'] = modelbreach['score'] if 'score' in modelbreach else 'No Darktrace model breach score'
    relevant_info['triggeredComponents'] = modelbreach['triggeredComponents'] if 'triggeredComponents' in modelbreach\
        else 'No Darktrace triggeredComponents'

    if 'device' in modelbreach:
        device = modelbreach['device']
        device_info = {}
        device_info['did'] = str(device['did']) if 'did' in device else 'No DID in Darktrace'
        device_info['macaddress'] = device['macaddress'] if 'macaddress' in device else 'No MAC address in Darktrace'
        device_info['vendor'] = device['vendor'] if 'vendor' in device and device['vendor'] != '' \
            else 'No device vendor in Darktrace'
        device_info['ip'] = device['ip'] if 'ip' in device else 'No device IP in Darktrace'
        device_info['hostname'] = device['hostname'] if 'hostname' in device else 'No device hostname in Darktrace'
        device_info['devicelabel'] = device['devicelabel']if 'devicelabel' in device else 'No device label in Darktrace'
    relevant_info['device'] = device_info

    if 'then' in modelbreach['model']:
        modelthen = modelbreach['model']['then']
        model_info = {}
        model_info['name'] = modelthen['name'] if 'name' in modelthen else 'No Darktrace model name'
        model_info['pid'] = modelthen['pid'] if 'pid' in modelthen else 'No Darktrace model pid'
        model_info['uuid'] = modelthen['uuid'] if 'uuid' in modelthen else 'No Darktrace model uuid'
        model_info['tags'] = modelthen['tags'] if 'tags' in modelthen else 'No Darktrace model tags'
        model_info['priority'] = modelthen['priority'] if 'priority' in modelthen else 'No Darktrace model priority'
        model_info['description'] = modelthen['description'] if 'description' in modelthen else 'No Darktrace model description'

    relevant_info['model'] = model_info

    return relevant_info


def format_JSON_for_modelbreach(modelbreach: Dict[str, Any]) -> Dict[str, Any]:
    """Formats JSON for get-breach command
    :type modelbreach: ``Dict[str, Any]``
    :param modelbreach: JSON model breach as returned by API for fetch incident
    :return: Filtered JSON containing only relevant fields for context
    :rtype: ``Dict[str, Any]``
    """
    relevant_info = {}

    relevant_info['commentCount'] = modelbreach['commentCount'] if 'commentCount' in modelbreach else 'No comment count'
    relevant_info['pbid'] = modelbreach['pbid'] if 'pbid' in modelbreach else 'No Darktrace pbid'
    relevant_info['time'] = modelbreach['time'] if 'time' in modelbreach else 'No Darktrace model breach time'
    relevant_info['score'] = modelbreach['score'] if 'score' in modelbreach else 'No Darktrace model breach score'

    if 'device' in modelbreach:
        device = modelbreach['device']
        device_info = {}
        device_info['did'] = str(device['did']) if 'did' in device else 'No DID in Darktrace'
        device_info['macaddress'] = device['macaddress'] if 'macaddress' in device else 'No MAC address in Darktrace'
        device_info['vendor'] = device['vendor'] if 'vendor' in device and device['vendor'] != '' \
            else 'No device vendor in Darktrace'
        device_info['ip'] = device['ip'] if 'ip' in device else 'No device IP in Darktrace'
        device_info['hostname'] = device['hostname'] if 'hostname' in device else 'No device hostname in Darktrace'
        device_info['devicelabel'] = device['devicelabel']if 'devicelabel' in device else 'No device label in Darktrace'
    relevant_info['device'] = device_info

    if 'then' in modelbreach['model']:
        modelthen = modelbreach['model']['then']
        model_info = {}
        model_info['name'] = modelthen['name'] if 'name' in modelthen else 'No Darktrace model name'
        model_info['pid'] = modelthen['pid'] if 'pid' in modelthen else 'No Darktrace model pid'
        model_info['uuid'] = modelthen['uuid'] if 'uuid' in modelthen else 'No Darktrace model uuid'
        model_info['tags'] = modelthen['tags'] if len(modelthen['tags']) > 0 else ['No Darktrace model tags']
        model_info['priority'] = modelthen['priority'] if 'priority' in modelthen else 'No Darktrace model priority'
        model_info['description'] = modelthen['description'] if 'description' in modelthen else 'No Darktrace model description'

    relevant_info['model'] = model_info

    return relevant_info


"""*****COMMAND FUNCTIONS****"""


def test_module(client: Client, first_fetch_time: Optional[int]) -> str:
    """
    Returning 'ok' indicates that the integration works like it is supposed to. Connection to the service is successful.

    :type client: ``Client``
    :param client:
        Darktrace Client
    :type first_fetch_time: ``Optional[int]``
    :param first_fetch_time:
        First fetch time
    :return:
        A message to indicate the integration works as it is supposed to
    :rtype: ``str``
    """
    try:
        client.search_modelbreaches(min_score=0, start_time=first_fetch_time)

    except DemistoException as e:
        if 'Forbidden' in str(e):
            return 'Authorization Error: make sure API Key is correctly set'
        else:
            raise e
    return 'ok'


def fetch_incidents(client: Client, max_alerts: int, last_run: Dict[str, int],
                    first_fetch_time: Optional[int], min_score: int) -> Tuple[Dict[str, int], List[dict]]:
    """This function retrieves new model breaches every minute. It will use last_run
    to save the timestamp of the last incident it processed. If last_run is not provided,
    it should use the integration parameter first_fetch to determine when to start fetching
    the first time.
    :type client: ``Client``
    :param Client: Darktrace client to use
    :type max_alerts: ``int``
    :param max_alerts: Maximum numbers of incidents per fetch
    :type last_run: ``Dict[str, int]``
    :param last_run:
        A dict with a key containing the latest incident created time we got
        from last fetch
    :type first_fetch_time: ``Optional[int]``
    :param first_fetch_time:
        If last_run is None (first time we are fetching), it contains
        the timestamp in milliseconds on when to start fetching incidents
    :type min_score: ``int``
    :param min_score:
        min_score of model breaches to pull. Range is [0,100]
    :return:
        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
    :rtype: ``Tuple[Dict[str, int], List[dict]]``
    """

    # Get the last fetch time, if exists
    # last_run is a dict with a single key, called last_fetch
    last_fetch = last_run.get('last_fetch', None)
    # Handle first fetch time
    if last_fetch is None:
        last_fetch = first_fetch_time
    else:
        last_fetch = int(last_fetch)

    # for type checking, making sure that latest_created_time is int
    latest_created_time = cast(int, last_fetch)

    # Each incident is a dict with a string as a key
    incidents: List[Dict[str, Any]] = []

    alerts = client.search_modelbreaches(
        min_score=min_score / 100,    # Scale the min score from [0,100] to [0 to 1] for API calls
        start_time=last_fetch       # time of last fetch or initialization time
    )

    for alert in alerts:
        # If no created_time set is as epoch (0). We use time in ms, which
        # matches the Darktrace API response
        incident_created_time = int(alert.get('time', '0'))
        alert['time'] = timestamp_to_datestring(incident_created_time)

        # to prevent duplicates, we are only adding incidents with creation_time > last fetched incident
        if last_fetch:
            if incident_created_time <= last_fetch:
                continue

        incident_name = 'Darktrace Model Breach #' + str(alert['pbid'])

        formatted_JSON = format_JSON_for_fetch_incidents(alert)

        # The incident dict is initialized with a few mandatory fields:
        # name: the incident name
        # occurred: the time on when the incident occurred, in ISO8601 format
        # we use timestamp_to_datestring() from CommonServerPython.py to
        # handle the conversion.
        # rawJSON: everything else is packed in a string via json.dumps()
        # and is included in rawJSON. It will be used later for classification
        # and mapping inside XSOAR.
        # severity: it's not mandatory, but is recommended. It must be
        # converted to XSOAR specific severity (int 1 to 4)

        incident = {
            'name': incident_name,
            'occurred': timestamp_to_datestring(incident_created_time),
            'rawJSON': json.dumps(formatted_JSON)
        }

        incidents.append(incident)

        # Update last run and add incident if the incident is newer than last fetch
        if incident_created_time > latest_created_time:
            latest_created_time = incident_created_time

        if len(incidents) >= max_alerts:
            break

    # Save the next_run as a dict with the last_fetch key to be stored
    next_run = {'last_fetch': latest_created_time}
    return next_run, incidents


def get_breach_command(client: Client, args: Dict[str, Any]) -> CommandResults:
    """darktrace-get-breach command: Returns a Darktrace model breach

    :type client: ``Client``
    :param Client: Darktrace client to use

    :type args: ``Dict[str, Any]``
    :param args:
        all command arguments, usually passed from ``demisto.args()``.
        ``args['alert_id']`` alert ID to return

    :return:
        A ``CommandResults`` object that is then passed to ``return_results``,
        that contains an alert

    :rtype: ``CommandResults``
    """

    pbid = str(args.get('pbid', None))
    if not pbid:
        raise ValueError('Darktrace Model Breach ID not specified')

    model_breach = client.get_modelbreach(pbid=pbid)

    if 'time' in model_breach:
        created_time = int(model_breach.get('time', '0'))
        model_breach['time'] = timestamp_to_datestring(created_time)

    # Format JSON for Context Output
    formatted_output = format_JSON_for_modelbreach(model_breach)

    readable_output = tableToMarkdown(f'Darktrace Model Breach {pbid}', formatted_output)

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix='Darktrace.ModelBreach',
        outputs_key_field='pbid',
        outputs=formatted_output
    )


def get_breach_details_command(client: Client, args: Dict[str, Any]) -> CommandResults:

    pbid = str(args.get('pbid', None))
    if not pbid:
        raise ValueError('Darktrace Model Breach ID not specified')

    endtime = str(args.get('endtime', None))
    count = str(int(args.get('count', None)) + 1)
    offset = str(args.get('offset', None))

    model_breach = (client.get_modelbreach_details(pbid=pbid, endtime=endtime, count=count, offset=offset))[1:]

    if 'time' in model_breach:
        created_time = int(model_breach.get('time', '0'))
        model_breach['time'] = timestamp_to_datestring(created_time)

    headers = []
    for event in model_breach:
        for head in event.keys():
            headers.append(head)

    headers = list(set(headers))
    headers = sorted(headers)

    readable_output = tableToMarkdown(f'Darktrace Model Breach {pbid} Details', model_breach, headers=headers,
                                      removeNull=True)

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix='Darktrace.ModelBreach',
        outputs_key_field='pid',
        outputs=model_breach
    )


def get_model_command(client: Client, args: Dict[str, Any]) -> CommandResults:
    uuid = str(args.get('uuid', None))
    if not uuid:
        raise ValueError('Darktrace Model UUID not specified')

    res = client.get_model(uuid=uuid)
    readable_output = tableToMarkdown(f'Darktrace Model {uuid}', res)

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix='Darktrace.Model',
        outputs_key_field='uuid',
        outputs=res
    )


def get_component_command(client: Client, args: Dict[str, Any]) -> CommandResults:
    cid = str(args.get('cid', None))
    if not cid:
        raise ValueError('Darktrace Component CID not specified')

    res = client.get_component(cid=cid)
    readable_output = tableToMarkdown(f'Darktrace Component {cid}', res)

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix='Darktrace.Component',
        outputs_key_field='cid',
        outputs=res
    )


def get_comments_command(client: Client, args: Dict[str, Any]) -> CommandResults:
    """darktrace-get-comments command: Returns the comments on the model breach

    :type client: ``Client``
    :param Client: Darktrace client to use

    :type args: ``Dict[str, Any]``
    :param args:
        all command arguments, usually passed from ``demisto.args()``.
        ``args['alert_id']`` alert ID to return

    :return:
        A ``CommandResults`` object that is then passed to ``return_results``,
        that contains an alert

    :rtype: ``CommandResults``
    """

    pbid = str(args.get('pbid', None))
    if not pbid:
        raise ValueError('Darktrace Model Breach ID not specified')

    comments = client.get_modelbreach_comments(pbid=pbid)
    model_breach_comments = {"comments": comments}

    if len(comments) == 0:
        model_breach_comments["comments"] = [{"message": "No comments in Darktrace on this model breach."}]

    for comment in model_breach_comments["comments"]:
        if 'time' in comment:
            created_time = int(comment.get('time', '0'))
            comment['time'] = timestamp_to_datestring(created_time)
        comment['pbid'] = int(pbid)

    readable_output = tableToMarkdown(f'Darktrace Model Breach {pbid} Comments', model_breach_comments["comments"])

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix='Darktrace.ModelBreach',
        outputs_key_field='pid',
        outputs=model_breach_comments
    )


def acknowledge_breach_command(client: Client, args: Dict[str, Any]) -> CommandResults:
    """acknowledge_breach_command: Acknowledges the model breach based on pbid

    :type client: ``Client``
    :param Client: Darktrace client to use

    :type args: ``Dict[str, Any]``
    :param args:
        all command arguments, usually passed from ``demisto.args()``.
        ``args['alert_id']`` alert ID to return

    :return:
        A ``CommandResults`` object that is then passed to ``return_results``,
        that contains an alert

    :rtype: ``CommandResults``
    """
    pbid = str(args.get('pbid', None))
    if not pbid:
        raise ValueError('Darktrace Model Breach ID not specified')

    ack_response = client.acknowledge_breach(pbid=pbid)
    if ack_response["response"] != "SUCCESS":
        ack_response["response"] = "Model Breach already acknowledged."
    else:
        ack_response["response"] = "Successfully acknowledged."
    ack_output: Dict[str, Any] = {}
    ack_output['pbid'] = int(pbid)
    ack_output['acknowledged'] = "true"
    readable_output = tableToMarkdown(f'Model Breach {pbid} Acknowledged', ack_response)

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix='Darktrace.ModelBreach',
        outputs_key_field='pbid',
        outputs=ack_output
    )


def unacknowledge_breach_command(client: Client, args: Dict[str, Any]) -> CommandResults:
    """acknowledge_breach_command: Unacknowledges the model breach based on pbid

    :type client: ``Client``
    :param Client: Darktrace client to use

    :type args: ``Dict[str, Any]``
    :param args:
        all command arguments, usually passed from ``demisto.args()``.
        ``args['alert_id']`` alert ID to return

    :return:
        A ``CommandResults`` object that is then passed to ``return_results``,
        that contains an alert

    :rtype: ``CommandResults``
    """
    pbid = str(args.get('pbid', None))
    if not pbid:
        raise ValueError('Darktrace Model Breach ID not specified')

    ack_response = client.unacknowledge_breach(pbid=pbid)
    if ack_response["response"] != "SUCCESS":
        ack_response["response"] = "Model Breach already unacknowledged."
    else:
        ack_response["response"] = "Successfully unacknowledged."
    ack_output: Dict[str, Any] = {}
    ack_output['pbid'] = int(pbid)
    ack_output['acknowledged'] = "false"
    readable_output = tableToMarkdown(f'Model Breach {pbid} Acknowledged', ack_response)

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix='Darktrace.ModelBreach',
        outputs_key_field='pbid',
        outputs=ack_output
    )


def list_similar_devices_command(client: Client, args: Dict[str, Any]) -> CommandResults:
    """list_similar_devices_command: Returns a list of similar devices to a device specified
    by Darktrace DID

    :type client: ``Client``
    :param Client: Darktrace client to use

    :type args: ``Dict[str, Any]``
    :param args:
        all command arguments, usually passed from ``demisto.args()``.
        ``args['alert_id']`` alert ID to return

    :return:
        A ``CommandResults`` object that is then passed to ``return_results``,
        that contains an alert

    :rtype: ``CommandResults``
    """
    did = str(args.get('did', None))
    max_results = str(args.get('max_results', 5))
    if not did:
        raise ValueError('Darktrace Device ID not specified')

    similar_devices = client.list_similar_devices(did=did, max_results=max_results)
    for device in similar_devices:
        if (device['firstSeen']):
            device['firstSeen'] = timestamp_to_datestring(device['firstSeen'])
        if (device['lastSeen']):
            device['lastSeen'] = timestamp_to_datestring(device['lastSeen'])

    readable_output = tableToMarkdown(f'List of similar devices to device:{did}:', similar_devices)
    formatted_output = {'did': int(did), 'devices': similar_devices}

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix='Darktrace.SimilarDevices',
        outputs_key_field='did',
        outputs=formatted_output
    )


def get_external_endpoint_details_command(client: Client, args: Dict[str, Any]) -> CommandResults:
    """get_external_endpoint_details_command: Returns information about a specified external endpoint

    :type client: ``Client``
    :param Client: Darktrace client to use

    :type args: ``Dict[str, Any]``
    :param args:
        all command arguments, usually passed from ``demisto.args()``.
        ``args['alert_id']`` alert ID to return

    :return:
        A ``CommandResults`` object that is then passed to ``return_results``,
        that contains an alert

    :rtype: ``CommandResults``
    """
    endpoint_type = str(args.get('endpoint_type'))
    endpoint_value = str(args.get('endpoint_value'))
    additional_info = str(args.get('additional_info'))
    devices = str(args.get('devices'))
    score = str(args.get('score'))

    endpoint_details = client.get_external_endpoint_details(endpoint_type, endpoint_value, additional_info, devices, score)

    if endpoint_details:
        if endpoint_details['firsttime']:
            endpoint_details['firsttime'] = timestamp_to_datestring(endpoint_details['firsttime'])
        readable_output = tableToMarkdown(f'{endpoint_type.capitalize()}: {endpoint_value} details', endpoint_details)
        formatted_output = endpoint_details
    else:
        readable_output = f'### Did not get any details for {endpoint_type}:{endpoint_value}'
        formatted_output = {}

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix='Darktrace.ExternalEndpointDetails',
        outputs=formatted_output
    )


def get_device_connection_info_command(client: Client, args: Dict[str, Any]) -> CommandResults:
    """get_device_connection_info_command: Returns graphing connection information about a specified device

    :type client: ``Client``
    :param Client: Darktrace client to use

    :type args: ``Dict[str, Any]``
    :param args:
        all command arguments, usually passed from ``demisto.args()``.
        ``args['alert_id']`` alert ID to return

    :return:
        A ``CommandResults`` object that is then passed to ``return_results``,
        that contains an alert

    :rtype: ``CommandResults``
    """
    did = args.get('did')
    data_type = args.get('data_type')
    external_domain = args.get('external_domain')
    destination_did = args.get('destination_did')
    show_all_graph_data = args.get('show_all_graph_data', 'false')
    full_device_details = args.get('full_device_details', 'false')
    num_similar_devices = args.get('num_similar_devices')
    device_info_response = client.get_device_connection_info(did, data_type, external_domain, destination_did,
                                                             show_all_graph_data, full_device_details,
                                                             num_similar_devices)
    if device_info_response:
        readable_output = tableToMarkdown(f'Results for device id: {did}', device_info_response)
        formatted_output = device_info_response
    else:
        readable_output = '### No results were found for the given ID'
        formatted_output = {}

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix='Darktrace.DeviceConnectionInfo',
        outputs=formatted_output
    )


def get_device_identity_info_command(client: Client, args: Dict[str, Any]) -> CommandResults:
    """get_device_identity_info_command: Returns identifying information about a specified device

    :type client: ``Client``
    :param Client: Darktrace client to use

    :type args: ``Dict[str, Any]``
    :param args:
        all command arguments, usually passed from ``demisto.args()``.
        ``args['alert_id']`` alert ID to return

    :return:
        A ``CommandResults`` object that is then passed to ``return_results``,
        that contains an alert

    :rtype: ``CommandResults``
    """
    max_results = args.get('max_results')
    order_by = args.get('order_by')
    order = args.get('order')
    query = args.get('query')
    device_info_response = client.get_device_identity_info(max_results, order_by, order, query)

    if device_info_response:
        formatted_output = device_info_response
        totalCount = formatted_output['totalCount'] if formatted_output['totalCount'] else 0
        if formatted_output['devices']:
            numResults = len(formatted_output['devices'])
            formatted_output['displayedCount'] = numResults
            for device in formatted_output['devices']:
                if 'firstSeen' in device:
                    device['firstSeen'] = timestamp_to_datestring(device['firstSeen'])
                if 'lastSeen' in device:
                    device['lastSeen'] = timestamp_to_datestring(device['lastSeen'])
            readable_output = tableToMarkdown(f'Results for query: {query} '
                                              f'({numResults} results displayed of {totalCount} which match the query)',
                                              formatted_output['devices'])
        else:
            readable_output = tableToMarkdown(f'Results for query: {query}', formatted_output)
    else:
        readable_output = '### No results were found for the given query'
        formatted_output = {}

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix='Darktrace.DeviceIdentityInfo',
        outputs_key_field='devices.did',
        outputs=formatted_output
    )


def get_entity_details_command(client: Client, args: Dict[str, Any]) -> CommandResults:
    """get_entity_details_command: Returns a time sorted list of connections and events for a device
    or an entity such as a user credential.

    :type client: ``Client``
    :param Client: Darktrace client to use

    :type args: ``Dict[str, Any]``
    :param args:
        all command arguments, usually passed from ``demisto.args()``.
        ``args['alert_id']`` alert ID to return

    :return:
        A ``CommandResults`` object that is then passed to ``return_results``,
        that contains an alert

    :rtype: ``CommandResults``
    """
    max_results = min(50, int(args.get('max_results', 50)))
    offset = int(args.get('offset', 0))
    query_list = argToList(args.get('query'))

    truncated_resp, resp = client.get_entity_details(max_results, offset, query_list)
    if truncated_resp:
        if "device" in truncated_resp[0]:
            readable_output = tableToMarkdown('Results:', truncated_resp[1:])
        else:
            readable_output = tableToMarkdown('Results:', truncated_resp)
        formatted_output = truncated_resp
    else:
        readable_output = 'No details were retrieved for the given parameters'
        formatted_output = {}

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix='Darktrace.EntityDetails',
        outputs=formatted_output
    )


"""*****MAIN FUNCTIONS****
Takes care of reading the integration parameters via
the ``demisto.params()`` function, initializes the Client class and checks the
different options provided to ``demisto.commands()``, to invoke the correct
command function passing to it ``demisto.args()`` and returning the data to
``return_results()``. If implemented, ``main()`` also invokes the function
``fetch_incidents()``with the right parameters and passes the outputs to the
``demisto.incidents()`` function. ``main()`` also catches exceptions and
returns an error message via ``return_error()``.
"""


def main() -> None:
    """main function, parses params and runs command functions
    :return:
    :rtype:
    """

    # Collect Darktrace URL
    base_url = demisto.params().get('url')

    # Collect API tokens
    public_api_token = demisto.params().get('public_api_token', '') or demisto.params().get('public_creds', {}).get('password')
    private_api_token = demisto.params().get('private_api_token', '') or demisto.params().get('private_creds', {}).get('password')
    tokens = (public_api_token, private_api_token)

    # Client class inherits from BaseClient, so SSL verification is
    # handled out of the box by it. Pass ``verify_certificate`` to
    # the Client constructor.
    verify_certificate = not demisto.params().get('insecure', False)

    # How much time before the first fetch to retrieve incidents
    first_fetch_time = arg_to_timestamp(
        arg=demisto.params().get('first_fetch', '1 day'),
        arg_name='First fetch time',
        required=True
    )

    # Client class inherits from BaseClient, so system proxy is handled
    # out of the box by it, just pass ``proxy`` to the Client constructor
    proxy = demisto.params().get('proxy', False)

    # ``demisto.debug()``, ``demisto.info()``, prints information in the XSOAR server log.
    demisto.debug(f'Command being called is {demisto.command()}')

    try:
        client = Client(
            base_url=base_url,
            verify=verify_certificate,
            proxy=proxy,
            auth=tokens
        )

        if demisto.command() == 'test-module':
            # This is the call made when pressing the integration Test button.
            result = test_module(client, first_fetch_time)
            return_results(result)

        elif demisto.command() == 'fetch-incidents':
            # Set and define the fetch incidents command to run after activated via integration settings.

            # Convert the argument to an int using helper function or set to MIN_SCORE_TO_FETCH
            min_score = arg_to_int(
                arg=demisto.params().get('min_score'),
                arg_name='min_score',
                required=False
            )
            if not min_score or min_score < MIN_SCORE_TO_FETCH:
                min_score = MIN_SCORE_TO_FETCH

            # Convert the argument to an int using helper function or set to MAX_INCIDENTS_TO_FETCH
            max_alerts = arg_to_int(
                arg=demisto.params().get('max_fetch', MAX_INCIDENTS_TO_FETCH),
                arg_name='max_fetch',
                required=False
            )
            if not max_alerts or max_alerts > MAX_INCIDENTS_TO_FETCH:
                max_alerts = MAX_INCIDENTS_TO_FETCH

            next_run, incidents = fetch_incidents(
                client=client,
                max_alerts=max_alerts,
                last_run=demisto.getLastRun(),  # getLastRun() gets the last run dict
                first_fetch_time=first_fetch_time,
                min_score=min_score
            )

            # Use the variables defined above as the outputs of fetch_incidents to set up the next call and create incidents:
            # saves next_run for the time fetch-incidents is invoked
            demisto.setLastRun(next_run)
            # fetch-incidents calls ``demisto.incidents()`` to provide the list
            # of incidents to create
            demisto.incidents(incidents)

        elif demisto.command() == 'darktrace-get-breach':
            return_results(get_breach_command(client, demisto.args()))

        elif demisto.command() == 'darktrace-get-breach-details':
            return_results(get_breach_details_command(client, demisto.args()))

        elif demisto.command() == 'darktrace-get-model':
            return_results(get_model_command(client, demisto.args()))

        elif demisto.command() == 'darktrace-get-component':
            return_results(get_component_command(client, demisto.args()))

        elif demisto.command() == 'darktrace-get-comments':
            return_results(get_comments_command(client, demisto.args()))

        elif demisto.command() == 'darktrace-acknowledge':
            return_results(acknowledge_breach_command(client, demisto.args()))

        elif demisto.command() == 'darktrace-unacknowledge':
            return_results(unacknowledge_breach_command(client, demisto.args()))

        elif demisto.command() == 'darktrace-list-similar-devices':
            return_results(list_similar_devices_command(client, demisto.args()))

        elif demisto.command() == 'darktrace-get-external-endpoint-details':
            return_results(get_external_endpoint_details_command(client, demisto.args()))

        elif demisto.command() == 'darktrace-get-device-connection-info':
            return_results(get_device_connection_info_command(client, demisto.args()))

        elif demisto.command() == 'darktrace-get-device-identity-info':
            return_results(get_device_identity_info_command(client, demisto.args()))

        elif demisto.command() == 'darktrace-get-entity-details':
            return_results(get_entity_details_command(client, demisto.args()))

    except Exception as e:
        demisto.error(traceback.format_exc())  # print the traceback
        return_error(f'Failed to execute {demisto.command()} command.\nError:\n{str(e)}')


"""*****ENTRY POINT****"""
if __name__ in ('__main__', '__builtin__', 'builtins'):
    main()