DarktraceAdmin

This pack includes configurations to combine the world-class threat detection of Darktrace with the synchrony and automation abilities of XSOAR, allowing security teams to manage device actions including device statuses and tags. Your understanding of potential threats can also be levelled-up with Advanced Search logs from DPI.

Network Security · Darktrace

Details

IDDarktraceAdmin
ProviderThoma Bravo
CategoryNetwork Security
From Version6.6.0
Docker Imagedemisto/python3:3.12.13.10116658
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 6.0.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.

Base Command

darktrace-get-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.deviceId 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-get-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-run-advanced-search-analysis


Runs advanced search analysis queries.

Base Command

darktrace-run-advanced-search-analysis

Input

Argument Name Description Required
initialDate initial date for query (YYYY-MM-DD) Required
initialTime initial time for query (HH:MM:SS) Required
endDate end date for query (YYYY-MM-DD) Required
endTime end time for query (HH:MM:SS) Required
query enter an advanced search query Required
operation enter an advanced search operation to perform on query results metric Required
metric enter an advanced search analysis metric Required
offset analyses 10k connections at a time, use this parameter to analyse further results Default

Context Output

Path Type Description
Darktrace.AdvancedSearch Dictionary Advanced Search Results

darktrace-post-to-watched-list


Posts hostnames and ips to the Darktrace Watched Domain List.

Base Command

darktrace-post-to-watched-list

Input

Argument Name Description Required
endpointsToWatch Unique or Comma separated list of ips, hostnames or domains to watch Required
description Provide an optional description for added entries Optional

Context Output

Path Type Description
Darktrace.Endpoint.Watched String Whether the device has been successfully tagged

darktrace-get-tagged-devices


Returns all Darktrace tagged devices

Base Command

darktrace-get-tagged-devices

Input

Argument Name Description Required
tagName Tag name Required

Context Output

Path Type Description
Darktrace.Device.deviceId Number Device unique identifier
Darktrace.Device.hostname String Device Hostname
Darktrace.Device.label String device label
Darktrace.Device.credentials Unknown credentials seen on device
Darktrace.Device.otherTags Unknown other tags found on device

darktrace-get-tags-for-device


Returns all tags present on a specified device.

Base Command

darktrace-get-tags-for-device

Input

Argument Name Description Required
deviceId Device unique identifier Required

Context Output

Path Type Description
Darktrace.Device.tagId Number Tag Id
Darktrace.Device.tagName String Tag Name
Darktrace.Device.tagDescription String Tag description if present
Darktrace.Device.expiry Number Tag expiration if applicable

darktrace-post-tag-to-device


Posts a tag to a device

Base Command

darktrace-post-tag-to-device

Input

Argument Name Description Required
deviceId Device unique identifier Required
tagName Tag name to be applied Required

Context Output

Path Type Description
Darktrace.Device.tagId Number Tag Id
Darktrace.Device.tagName String Tag Name
Darktrace.Device.deviceId Number Device unique identifier
Darktrace.Device.response String POST action message response

Configuration parameters

  • url — Server URL (e.g. https://example.net) (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • incidentType — Incident type
  • publicApiKey — Public API Token (required)
  • privateApiKey — Private API Token (required)

Commands (8)

  • darktrace-get-device-connection-info

    Returns the graphable data for a device (max of 4 weeks of connections retained).

  • darktrace-get-external-endpoint-details

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

  • darktrace-get-similar-devices

    Returns a list of similar devices with respect to a specified device.

  • darktrace-get-tagged-devices

    Get all devices that hold a common tag.

  • darktrace-get-tags-for-device

    Get all tags for the given device.

  • darktrace-post-tag-to-device

    Post a tag to a device.

  • darktrace-post-to-watched-list

    Post to the list of Darkace Watched Domains to generate alerts when visited.

  • darktrace-run-advanced-search-analysis

    Run an Advanced Search Query and perform an analysis operation on any metric.

import demistomock as demisto  # noqa: F401
from CommonServerPython import *  # noqa: F401
import hashlib
import hmac
import json
import traceback
from base64 import b64encode
from datetime import datetime, UTC
from typing import Any
from collections.abc import Mapping

import dateparser
import urllib3

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

# Disable insecure warnings
urllib3.disable_warnings()

"""*****CONSTANTS*****"""
DETAILS_ENDPOINT = "/details"
LIST_SIMILAR_DEVICES_ENDPOINT = "/similardevices"
EXTERNAL_ENDPOINT = "/endpointdetails"
DEVICE_INFO_ENDPOINT = "/deviceinfo"
DEVICE_IDENTITY_ENDPOINT = "/devicesearch"
TAG_ENTITIES_ENDPOINT = "/tags/entities"
INTEL_FEED_ENDPOINT = "/intelfeed"
ADVANCED_SEARCH_ENDPOINT = "/advancedsearch/api/analyze"
STATUS_ENDPOINT = "/status?format=json"

PLEASE_CONTACT = "Please contact your Darktrace representative."

DARKTRACE_API_ERRORS = {
    "SIGNATURE_ERROR": "API Signature Error. You have invalid credentials in your config.",
    "DATE_ERROR": "API Date Error. Check that the time on this machine matches that of the Darktrace instance.",
    "ENDPOINT_ERROR": f"Invalid Endpoint. - {PLEASE_CONTACT}",
    "PRIVILEGE_ERROR": "User has insufficient permissions to access the API endpoint.",
    "UNDETERMINED_ERROR": f"Darktrace was unable to process your request - {PLEASE_CONTACT}",
    "FAILED_TO_PARSE": "N/A",
}

"""*****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(self, query_uri: str, params: dict[str, str] = None):
        """Handles Darktrace GET API calls"""
        return self._darktrace_api_call(query_uri, method="GET", params=params)

    def post(self, query_uri: str, data: dict = None, json: dict = None):
        """Handles Darktrace POST API calls"""
        return self._darktrace_api_call(query_uri, method="POST", data=data, json=json)

    def _darktrace_api_call(
        self,
        query_uri: str,
        method: str,
        params: dict = None,
        data: dict = None,
        json: dict = None,
        headers: dict[str, str] = None,
    ):
        """Handles Darktrace API calls"""
        headers = {
            **self._create_headers(query_uri, params or data or json or None, is_json=bool(json)),
            **(headers or {}),
        }

        try:
            res = self._http_request(
                method,
                url_suffix=query_uri,
                params=params,
                data=data,
                json_data=json,
                resp_type="response",
                headers=headers,
                error_handler=self.error_handler,
            )

            if res.status_code not in [200, 204]:
                raise Exception(
                    "Your request failed with the following error: "
                    + str(res.content)
                    + ". Response Status code: "
                    + str(res.status_code)
                )
        except Exception as e:
            raise Exception(e)
        try:
            return res.json()
        except Exception as e:
            raise ValueError(f"Failed to process the API response - {str(e)}")

    def error_handler(self, res: requests.Response):
        """Handles authentication errors"""
        if res.status_code == 400:
            values = res.json().values()
            if "API SIGNATURE ERROR" in values:
                raise Exception(DARKTRACE_API_ERRORS["SIGNATURE_ERROR"])
            elif "API DATE ERROR" in values:
                raise Exception(DARKTRACE_API_ERRORS["DATE_ERROR"])
        elif res.status_code == 302:
            # Valid hmac but invalid endpoint (should not happen)
            if res.text == "Found. Redirecting to /login":
                raise Exception(DARKTRACE_API_ERRORS["ENDPOINT_ERROR"])
            # Insufficient permissions but valid hmac
            elif res.text == "Found. Redirecting to /403":
                raise Exception(DARKTRACE_API_ERRORS["PRIVILEGE_ERROR"])
        elif res.status_code >= 300:
            raise Exception(DARKTRACE_API_ERRORS["UNDETERMINED_ERROR"])

    def _create_headers(self, query_uri: str, query_data: dict = None, is_json: bool = False) -> dict[str, str]:
        """Create headers required for successful authentication"""
        public_token, _ = self._auth
        date = (datetime.now(UTC)).isoformat(timespec="auto")
        signature = _create_signature(self._auth, query_uri, date, query_data, is_json=is_json)
        return {"DTAPI-Token": public_token, "DTAPI-Date": date, "DTAPI-Signature": signature}

    def run_advanced_search_analysis(self, query, metric, operation) -> dict[str, Any]:
        """Returns information from Darktrace advanced search analysis'
        :type query: ``str``
        :param query: Darktrace query string
        :type metric: ``str``
        :param metric: metric to be analyzed
        :type operation: ``str``
        :param operation: type of operation to perform
        :return: list containing advanced search analysis
        :rtype: ``Dict[str, Any]``
        """
        query_url_bytes = b64encode(str(json.dumps(query)).encode())
        query_url = query_url_bytes.decode()
        query_format = f"{ADVANCED_SEARCH_ENDPOINT}/{metric}/{operation}/{query_url}"
        return self.get(query_format)

    def get_device_connection_info(self, **query_params) -> dict[str, Any]:
        """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: dictionary containing lists of connection info
        :rtype: ``Dict[str, Any]``
        """
        query_uri = DEVICE_INFO_ENDPOINT
        return self.get(query_uri, query_params)

    def get_external_endpoint_details(self, endpoint_type, endpoint_value, additional_info, devices, score) -> dict[str, Any]:
        """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: ``Dict[str, Any]``
        """
        query_uri = EXTERNAL_ENDPOINT
        params = {endpoint_type: endpoint_value, "additionalinfo": additional_info, "devices": devices, "score": score}
        return self.get(query_uri, params)

    def get_similar_devices(self, did, max_results) -> list[dict[str, Any]]:
        """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]]``
        """
        query_uri = LIST_SIMILAR_DEVICES_ENDPOINT
        params = {"did": did, "count": max_results}
        return self.get(query_uri, params)

    def post_to_watched_list(self, addlist, description) -> dict[str, Any]:
        """Returns information from POST endpoints to watched list advanced search analysis
        :type addlist: ``List[str]``
        :param addlist: Darktrace query string
        :type description: ``str``
        :param description: description commit message
        :return: dict containing post result
        :rtype: ``Dict[str, Any]``
        """
        json = {"addlist": addlist}
        if description:
            json["description"] = description
        return self.post(INTEL_FEED_ENDPOINT, json=json)

    def get_tagged_devices(self, tag_name) -> dict[str, Any]:
        """Returns information from devices given a tag as a filter
        :type tag_name: ``str``
        :param tag_name: tag name to query from
        :return: dict containing devices that hold a certain tag
        :rtype: ``List[Dict[str, Any]]``
        """
        params = {"tag": tag_name, "fulldevicedetails": "true"}
        return self.get(TAG_ENTITIES_ENDPOINT, params=params)

    def get_tags_for_device(self, did) -> list[dict[str, Any]]:
        """Returns tags for a certain device
        :type did: ``str``
        :param did: device id
        :return: list containing tag information for a device
        :rtype: ``List[Dict[str, Any]]``
        """
        return self.get(TAG_ENTITIES_ENDPOINT, params={"did": did})

    def post_tag_to_device(self, did, tag_name) -> dict[str, Any]:
        """Returns response from post command
        :type did: ``str``
        :param did: device id
        :type tag_name: ``str``
        :param tag_name: tag name to add to device
        :return: dict containing response from POST request
        :rtype: ``Dict[str, Any]``
        """
        data = {"did": did, "tag": tag_name}
        return self.post(TAG_ENTITIES_ENDPOINT, data)

    def check_status(self) -> dict:
        """Checks a valid api request to the status endpoint
        :return: dict containing status data
        :rtype: ``Dict``
        """
        return self.get(STATUS_ENDPOINT)


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


def arg_to_timestamp(arg: Any, arg_name: str, required: bool = False) -> int | None:
    """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 stringify_data(data: Mapping) -> str:
    """Stringify a params or data dict without encoding"""
    return "&".join([f"{k}={v}" for k, v in data.items()])


def _create_signature(tokens: tuple, query_uri: str, date: str, query_data: dict = None, is_json: bool = False) -> str:
    """Create signature from Darktrace private token"""
    public_token, private_token = tokens
    if is_json:
        query_string = f"?{json.dumps(query_data)}"
    else:
        query_string = f"?{stringify_data(query_data)}" if query_data else ""

    return hmac.new(
        private_token.encode("ASCII"),
        f"{query_uri}{query_string}\n{public_token}\n{date}".encode("ASCII"),
        hashlib.sha1,
    ).hexdigest()


def arg_to_dict(arg, separator=","):
    """Transforms str of an arg into a dict"""
    if not arg:
        return {}
    result = dict(subString.split("=") for subString in (arg).split(separator))
    return result


def check_required_fields(args, *fields):
    """Checks that required fields are found, raises a value error otherwise"""
    for field in fields:
        if field not in args:
            raise ValueError(f"Argument error could not find {field} in {args}")


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


def test_module(client: Client) -> 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.check_status()

    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 run_advanced_search_analysis_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """run_advanced_search_analysis_command: Runs an advanced search analysis on a specific query and metric and applies an
    operation to returned a list of results.

    :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``
    """
    check_required_fields(
        args, "offset", "initialDate", "initialTime", "endDate", "endTime", "query", "metric", "operation", "offset"
    )
    query = {
        "search": str(args["query"]),
        "fields": [],
        "offset": str(args["offset"]),
        "timeframe": "custom",
        "graphmode": "count",
        "time": {
            "from": f"{str(args['initialDate'])}T{str(args['initialTime'])}Z",
            "to": f"{str(args['endDate'])}T{str(args['endTime'])}Z",
            "user_interval": "0",
        },
        "mode": "",
        "analyze_field": "",
    }
    adv_search_reponse = client.run_advanced_search_analysis(query, str(args["metric"]), str(args["operation"]))
    check_required_fields(adv_search_reponse, "hits")
    hits = adv_search_reponse["hits"]
    formatted_output = hits["hits"]
    readable_output = tableToMarkdown(
        "Results for Advanced Search Query ", formatted_output, headers=("id", "count"), headerTransform=string_to_table_header
    )
    return CommandResults(readable_output=readable_output, outputs_prefix="Darktrace.AdvancedSearch", 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``
    """
    check_required_fields(args, "deviceId", "dataType")
    did = args["deviceId"]
    show_all_graph_data = args.get("showAllGraphData", "false")
    full_device_details = args.get("fullDeviceDetails", "false")
    external_domain = args.get("externalDomain", "")
    destination_device_id = args.get("destinationDeviceId", "")
    num_similar_devices = args.get("numberOfSimilarDevices", 0)
    device_info_response = client.get_device_connection_info(
        did=did,
        datatype=args["dataType"],
        externaldomain=external_domain,
        oid=destination_device_id,
        showallgraphdata=show_all_graph_data,
        fulldevicedetails=full_device_details,
        similardevices=num_similar_devices,
    )
    if device_info_response["deviceInfo"]:
        readable_output = tableToMarkdown(f"Results for device id: {did}", device_info_response["deviceInfo"])
        formatted_output = device_info_response["deviceInfo"]
    else:
        readable_output = "### No results were found for the given ID"
        formatted_output = readable_output

    return CommandResults(
        readable_output=readable_output, outputs_prefix="Darktrace.DeviceConnectionInfo", 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``
    """
    check_required_fields(args, "endpointType", "endpointValue")
    endpoint_type = str(args["endpointType"])
    endpoint_value = str(args["endpointValue"])

    endpoint_details = client.get_external_endpoint_details(
        endpoint_type, endpoint_value, str(args["additionalInfo"]), str(args["devices"]), str(args["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_similar_devices_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """get_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``
    """
    check_required_fields(args, "deviceId")
    did = str(args.get("deviceId", None))
    max_results = str(args.get("maxResults", 5))

    similar_devices = client.get_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 = {"deviceId": int(did), "devices": similar_devices}

    return CommandResults(
        readable_output=readable_output, outputs_prefix="Darktrace.Device", outputs_key_field="deviceId", outputs=formatted_output
    )


def post_to_watched_list_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """post_to_watched_list_command: Returns a response from posting domains or ips to the watched list domain

    :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``
    """
    check_required_fields(args, "endpointsToWatch")
    description = str(args.get("description"))
    intel_response = client.post_to_watched_list(addlist=str(args["endpointsToWatch"]), description=description)
    readable_data = tableToMarkdown("POST TAG to Device Response", intel_response)
    return CommandResults(readable_output=readable_data, outputs_prefix="Darktrace.Endpoint", outputs=intel_response)


def get_tagged_devices_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """get_tagged_devices_command: Gets a list of device information based on a common tag.

    :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``
    """
    check_required_fields(args, "tagName")
    tag_name = str(args["tagName"])
    device_info_response = client.get_tagged_devices(tag_name=tag_name)
    devices = device_info_response.get("devices")
    output: list = []
    if devices and len(devices):
        for device in devices:
            info: dict[str, Any] = {}
            info["deviceId"] = device["did"]
            info["hostname"] = device.get("hostname", "N/A")
            info["label"] = device.get("devicelabel", "N/A")
            info["credentials"] = device.get("credentials", "N/A")
            info["otherTags"] = device.get("tags", "N/A")
            output.append(info)
    else:
        output.append({"response": "Unable to locate any devices for the queried tag"})
    readable_output = tableToMarkdown(f"Device Tag Response for device: {tag_name} ", output)
    return CommandResults(
        readable_output=readable_output, outputs_prefix="Darktrace.Device", outputs_key_field="deviceId", outputs=output
    )


def get_tags_for_device_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """get_tags_for_device_command: Gets a list of tags for a 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``
    """
    check_required_fields(args, "deviceId")
    did = str(args["deviceId"])
    device_tags_response = client.get_tags_for_device(did=did)
    output: list = []
    for tag in device_tags_response:
        info: dict[str, Any] = {}
        info["tagId"] = tag["tid"]
        info["tagName"] = tag["name"]
        info["tagDescription"] = tag["data"]["description"]
        info["expiry"] = tag["expiry"]
        output.append(info)
    readable_output = tableToMarkdown(f"Device Tag Response for device: {did} ", output)
    return CommandResults(
        readable_output=readable_output, outputs_prefix="Darktrace.Device", outputs_key_field="tid", outputs=output
    )


def post_tag_to_device_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """post_tag_to_device_command: Posts a tag to a device and returns an action response

    :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``
    """
    check_required_fields(args, "deviceId", "tagName")
    tag_name = str(args["tagName"])
    did = str(args["deviceId"])
    post_tag_to_device_response = client.post_tag_to_device(did=did, tag_name=tag_name)
    output: dict[str, Any] = {}
    output["tagId"] = post_tag_to_device_response["tid"]
    output["tagName"] = tag_name
    output["deviceId"] = did
    output["response"] = "Successfully Tagged"
    readable_output = tableToMarkdown("Device Tag Response ", output)
    return CommandResults(
        readable_output=readable_output, outputs_prefix="Darktrace.Device", outputs_key_field="deviceId", outputs=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:  # pragma: no cover
    """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("publicApiKey")
    private_api_token = demisto.params().get("privateApiKey")
    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)

    # 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.
            return_results(test_module(client))

        elif demisto.command() == "darktrace-run-advanced-search-analysis":
            return_results(run_advanced_search_analysis_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-external-endpoint-details":
            return_results(get_external_endpoint_details_command(client, demisto.args()))

        elif demisto.command() == "darktrace-get-similar-devices":
            return_results(get_similar_devices_command(client, demisto.args()))

        elif demisto.command() == "darktrace-post-to-watched-list":
            return_results(post_to_watched_list_command(client, demisto.args()))

        elif demisto.command() == "darktrace-get-tagged-devices":
            return_results(get_tagged_devices_command(client, demisto.args()))

        elif demisto.command() == "darktrace-get-tags-for-device":
            return_results(get_tags_for_device_command(client, demisto.args()))

        elif demisto.command() == "darktrace-post-tag-to-device":
            return_results(post_tag_to_device_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()