CadoResponse

Automate data collection. Process data at cloud speed. Analyze with purpose.

Forensics & Malware Analysis · Cado Response

Details

IDCadoResponse
ProviderDarktrace
CategoryForensics & Malware Analysis
From Version6.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Cado Response Integration for Cortex XSOAR

Automate data collection. Process data at cloud speed. Analyze with purpose.
This integration was integrated and tested with version 1.2.0 of CadoResponse

Configure Cado Response in Cortex

Parameter Description Required
Cado Response Instance The URL for your Cado Response Instance True
API Key The API Key to use for connection True
Trust any certificate (not secure)   False
Use system proxy settings   False
Default Cado Response Project Provides a fallback Project ID from Cado Response if you forget to add an ID to a command. If you’re not sure, don’t change! True
Default AWS Region Provides a default AWS region to fallback to if you forget to add it to a command. True
Default S3 Bucket Provides a default S3 bucket to fallback to if you forget to add it to a command. True

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.

cado-create-project


Create a project in Cado Response

Base Command

cado-create-project

Input

Argument Name Description Required
project_name Name of the project. Required
project_description Description for the project. Optional

Context Output

Path Type Description
CadoResponse.Project.id Number The Project ID of the newly created project

Command example

!cado-create-project project_name="Project Name" description="Project Description"

Context Example

{
    "CadoResponse": {
        "Project": {
            "id": 1,
            "msg": "Created"
        }
    }
}

Human Readable Output

Results

id msg
1 Created

cado-list-project


Get a list of projects from Cado Response

Base Command

cado-list-project

Input

Argument Name Description Required
project_id ID of the project to retrive. Optional
limit Limit results to retrieve. Optional

Context Output

Path Type Description
CadoResponse.Projects.id Number ID of the retrieved project
CadoResponse.Projects.caseName String Name of the retrieved project
CadoResponse.Projects.description String Description of the retrieved project
CadoResponse.Projects.users Array Array of users assigned to the retrieved project
CadoResponse.Projects.created Date Creation date of the project

Command example

!cado-list-project limit=100

Context Example

{
    "CadoResponse": {
        "Projects": {
            "caseName": "Project Name_XSOAR",
            "created": "2022-01-17T12:21:46.613814",
            "deleted": false,
            "description": "This is a project in Cado Response created through Cortex XSOAR!",
            "id": 1,
            "status": "Pending",
            "users": [
                {
                    "display_name": "admin",
                    "id": 1,
                    "is_admin": true,
                    "login_type": 0,
                    "username": "admin"
                }
            ]
        }
    }
}

Human Readable Output

Results

caseName created deleted description id status users
Project Name_XSOAR 2022-01-17T12:21:46.613814 false This is a project in Cado Response created through Cortex XSOAR! 1 Pending {‘display_name’: ‘admin’, ‘id’: 1, ‘is_admin’: True, ‘login_type’: 0, ‘username’: ‘admin’}

Command example

!cado-list-project project_id=1

Context Example

{
    "CadoResponse": {
        "Projects": {
            "caseName": "Project Name_XSOAR",
            "created": "2022-01-17T12:21:46.613814",
            "deleted": false,
            "description": "This is a project in Cado Response created through Cortex XSOAR!",
            "id": 1,
            "status": "Pending",
            "users": [
                {
                    "display_name": "admin",
                    "id": 1,
                    "is_admin": true,
                    "login_type": 0,
                    "username": "admin"
                }
            ]
        }
    }
}

Human Readable Output

Results

caseName created deleted description id status users
Project Name_XSOAR 2022-01-17T12:21:46.613814 false This is a project in Cado Response created through Cortex XSOAR! 1 Pending {‘display_name’: ‘admin’, ‘id’: 1, ‘is_admin’: True, ‘login_type’: 0, ‘username’: ‘admin’}

cado-get-pipeline


Get pipeline details from Cado Response

Base Command

cado-get-pipeline

Input

Argument Name Description Required
pipeline_id The id of the pipeline to retrieve. Optional
project_id The id of the project the pipeline belongs to. Optional
limit Limit results to retrieve. Optional

Context Output

Path Type Description
CadoResponse.Pipeline.pipeline_id Number The ID of the retrieved pipeline
CadoResponse.Pipeline.pipeline_type String The type of pipeline that was retrieved
CadoResponse.Pipeline.created Date The date at which the retrieved pipeline was started
CadoResponse.Pipeline.evidence_id Number The evidence ID linked to the retrieved pipeline
CadoResponse.Pipeline.project_id Number The ID of the project the pipeline belongs to
CadoResponse.Pipeline.is_terminated Boolean A boolean which says if the retrieved pipeline has been finished/terminated
CadoResponse.Pipeline.summary Array An array of values containing the cancelled, failed, pending, running and successful pipeline subtasks
CadoResponse.Pipeline.subtask Array An array of tasks in the retrieved pipeline

Command example

!cado-get-pipeline project_id=1 pipeline_id=1

Context Example

{
    "CadoResponse": {
        "Pipeline": {
            "pipeline_id": 1,
   "pipeline_type": "processing",
      "created": "2022-01-17T12:22:00.843869",
      "evidence_id": 1,
      "project_id": 1,
      "is_terminated": false,
   "subtasks": [
         {
     "execution_duration": 0,
     "finish_time": 0,
     "name": "Triage: Attaching disk for local data storage.",
     "name_key": "infrastructure.check_ssd",
     "notification_level": "Info",
     "progress_text": [],
     "start_time": 0,
     "state": "PENDING",
     "task_id": "3699827f-63c4-4408-88a4-0ae899187ed3",
     "total_stages": null
      }
   ],
   "summary": {
     "cancelled": 0,
     "failure": 0,
     "pending": 14,
     "running": 0,
     "success": 0,
     "total": 14
   }
        }
    }

}

Human Readable Output

Results

pipeline_id pipeline_type created evidence_id project_id is_terminated summary subtask
1 processing 2022-01-17T12:22:00.843869 1 1 false “execution_duration”: 0,
“finish_time”: 0,
“name”: “Triage: attaching disk for local data storage.”,
“name_key”: infrastructure.check_ssd”,
“notification_level”: “Info”,
“progress_text”: [],
“start_time”: 0,
“state”: “PENDING”,
“task_id”: “3699827f-63c4-4408-88a4-0ae899187ed3”,
“total_stages”: null
“cancelled”: 0,
“failure”: 0,
“pending”: 14,
“running”: 0,
“success”: 0,
“total”: 14

cado-list-ec2


Get a list of EC2 instances in a region

Base Command

cado-list-ec2

Input

Argument Name Description Required
region The AWS region to list instances in. Required
project_id The ID of the project you wish to attach the acquisition to. Optional
limit Limit results to retrieve. Optional

Context Output

Path Type Description
CadoResponse.EC2Instances.id Number AWS ID of the EC2 Instance
CadoResponse.EC2Instances.instance_name String Name of the EC2 Instance
CadoResponse.EC2Instances.instance_type String AWS Type of the EC2 instance
CadoResponse.EC2Instances.region String AWS region of the EC2 instance

Command example

!cado-list-ec2 project_id=1 region="us-east-1" limit=100

Context Example

{
    "CadoResponse": {
        "EC2Instances": [
            {
                "_placement": "us-east-1c",
                "_state": "stopped",
                "celery_worker_name": null,
                "deployment_id": null,
                "evidence_id": null,
                "id": "i-00000000000",
                "instance_name": "Instance",
                "instance_type": "t3a.2xlarge",
                "ip_address": null,
                "launch_time": "Thu, 25 Mar 2021 18:38:13 GMT",
                "processing_type": null,
                "project_id": null,
                "queue_name": null,
                "region": {
                    "name": "us-east-1"
                },
                "worker_used": null
            }
        ]
    }
}

Human Readable Output

Results

_placement _state celery_worker_name deployment_id evidence_id id instance_name instance_type ip_address launch_time processing_type project_id queue_name region worker_used
us-east-1c stopped       i-00000000000 Instance t3a.2xlarge   Thu, 25 Mar 2021 18:38:13 GMT       name: us-east-1  

cado-list-s3


Get a list of S3 buckets

Base Command

cado-list-s3

Input

Argument Name Description Required
project_id The ID of the project you wish to attach the acquisition to. Optional
limit Limit results to retrieve. Optional

Context Output

Path Type Description
CadoResponse.S3Buckets.buckets Array An array of S3 buckets available to the project

Command example

!cado-list-s3 project_id=1 limit=100

Context Example

{
    "CadoResponse": {
        "S3Buckets": {
            "buckets": [
                "bucket",
            ]
        }
    }
}

Human Readable Output

Results

buckets
bucket

cado-trigger-ec2


Trigger a disk acquisition and processing pipeline in Cado Response for a given EC2 instance

Base Command

cado-trigger-ec2

Input

Argument Name Description Required
project_id The ID of the project you wish to attach the acquisition to. Optional
instance_id ID of the EC2 instance to acquire. Required
region AWS region in which the EC2 instance is located. Optional
bucket S3 bucket where the uploaded disk image resides. Optional
compress Flag indicating if disk compression is enabled. Optional
include_disks Flag indicating if we include disk images in the acquisition. Optional
include_hash Flag indicating if we calculate the hash of the disk. Optional
include_logs Flag indicating if we include system logs in the acquisition. Optional
include_screenshot Flag indicating if we include a screenshot of the system in the acquisition. Optional

Context Output

Path Type Description
CadoResponse.EC2Acquisition.pipeline_id Number ID of the created pipeline

Command example

!cado-trigger-ec2 project_id=1 instance_id="i-00000000000" region="us-east-1" bucket="bucket" compress=true include_disks=true include_hash=true include_logs=true include_screenshot=true

Context Example

{
    "CadoResponse": {
        "EC2Acquistion": {
            "created": "2022-01-17T12:21:59.084282",
            "evidence_id": 0,
            "name": "Acquiring i-00000000000",
            "pipeline_id": 1,
            "pipeline_type": "acquisition",
            "project_id": 1,
            "subtasks": [
                {
                    "id": "1587a9c9-c02c-464b-a6f7-d4b7e720bd93"
                },
                {
                    "id": "4f798bf8-c7d3-427c-9498-10a85cfe3978"
                },
                {
                    "id": "c5fa26f1-e282-47a6-8335-1160766e089b"
                },
                {
                    "id": "82ec9a7e-47ac-4539-9623-166a44a59d0f"
                },
                {
                    "id": "88151005-a999-422e-b4cb-9e76699d6e42"
                }
            ],
            "user_id": 1
        }
    }
}

Human Readable Output

Results

created evidence_id name pipeline_id pipeline_type project_id subtasks user_id
2022-01-17T12:21:59.084282 0 Acquiring i-00000000000 1 acquisition 1 {‘id’: ‘1587a9c9-c02c-464b-a6f7-d4b7e720bd93’},
{‘id’: ‘4f798bf8-c7d3-427c-9498-10a85cfe3978’},
{‘id’: ‘c5fa26f1-e282-47a6-8335-1160766e089b’},
{‘id’: ‘82ec9a7e-47ac-4539-9623-166a44a59d0f’},
{‘id’: ‘88151005-a999-422e-b4cb-9e76699d6e42’}
1

cado-trigger-s3


Trigger a disk acquisition and processing pipeline in Cado Response for a given file in an S3 bucket

Base Command

cado-trigger-s3

Input

Argument Name Description Required
project_id The ID of the project you wish to attach the acquisition to. Optional
bucket The S3 bucket name containing the file. Required
file_name The name of the file to process. Required

Context Output

Path Type Description
CadoResponse.S3Acquisition.pipeline_id Number ID of the created pipeline

Command example

!cado-trigger-s3 project_id=1 bucket="bucket" file_name="file"

Context Example

{
    "CadoResponse": {
        "S3Acquisition": {
            "created": "2022-01-17T12:22:00.843869",
            "evidence_id": 1,
            "name": "",
            "pipeline_id": 2,
            "pipeline_type": "processing",
            "project_id": 1,
            "subtasks": [
                {
                    "id": "3699827f-63c4-4408-88a4-0ae899187ed3"
                },
                {
                    "id": "727e2072-8bf7-4847-89ea-9447f5fd8fd0"
                },
                {
                    "id": "857d48b8-abaf-4ea6-b159-d25c9784b837"
                },
                {
                    "id": "533f7deb-74bc-4ffb-b81f-788ed714bead"
                },
                {
                    "id": "3f1defde-3986-4292-a423-1bef62d4c52b"
                },
                {
                    "id": "e41a0934-266b-4868-9a7d-5f083b1efcc1"
                },
                {
                    "id": "75411e10-46e9-41dd-8bf7-9b5fbdc8df71"
                },
                {
                    "id": "0afbf2f4-fbf3-4305-ad9f-b19d30f4b17c"
                },
                {
                    "id": "ca063c7b-1135-4922-8542-49f40ce71449"
                },
                {
                    "id": "67fdb0ea-dcee-4f65-a003-4f40fcd567fb"
                },
                {
                    "id": "1437ec33-6af2-4eb8-9c43-e071dcb7e0ac"
                },
                {
                    "id": "06db4dcc-57fd-48bc-bb34-5bd8f2da0a0d"
                },
                {
                    "id": "e3cc930e-9a60-46c3-97a1-611824c24437"
                },
                {
                    "id": "ad2c8877-39e7-4bff-9756-81278802ee76"
                }
            ],
            "user_id": 1
        }
    }
}

Human Readable Output

Results

created evidence_id name pipeline_id pipeline_type project_id subtasks user_id
2022-01-17T12:22:00.843869 1   2 processing 1 {‘id’: ‘3699827f-63c4-4408-88a4-0ae899187ed3’},
{‘id’: ‘727e2072-8bf7-4847-89ea-9447f5fd8fd0’},
{‘id’: ‘857d48b8-abaf-4ea6-b159-d25c9784b837’},
{‘id’: ‘533f7deb-74bc-4ffb-b81f-788ed714bead’},
{‘id’: ‘3f1defde-3986-4292-a423-1bef62d4c52b’},
{‘id’: ‘e41a0934-266b-4868-9a7d-5f083b1efcc1’},
{‘id’: ‘75411e10-46e9-41dd-8bf7-9b5fbdc8df71’},
{‘id’: ‘0afbf2f4-fbf3-4305-ad9f-b19d30f4b17c’},
{‘id’: ‘ca063c7b-1135-4922-8542-49f40ce71449’},
{‘id’: ‘67fdb0ea-dcee-4f65-a003-4f40fcd567fb’},
{‘id’: ‘1437ec33-6af2-4eb8-9c43-e071dcb7e0ac’},
{‘id’: ‘06db4dcc-57fd-48bc-bb34-5bd8f2da0a0d’},
{‘id’: ‘e3cc930e-9a60-46c3-97a1-611824c24437’},
{‘id’: ‘ad2c8877-39e7-4bff-9756-81278802ee76’}
1

Configuration parameters

  • url — Cado Response Instance (required)
  • apikey — API Key (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • CadoResponse_DefaultProject — Default Cado Response Project (required)
  • CadoResponse_DefaultRegion — Default AWS Region (required)
  • CadoResponse_DefaultBucket — Default S3 Bucket (required)

Commands (7)

  • cado-create-project

    Create a project in Cado Response.

  • cado-get-pipeline

    Get pipeline details from Cado Response.

  • cado-list-ec2

    Get a list of EC2 instances in a region.

  • cado-list-project

    Get a list of projects from Cado Response.

  • cado-list-s3

    Get a list of S3 buckets.

  • cado-trigger-ec2

    Trigger a disk acquisition and processing pipeline in Cado Response for a given EC2 instance.

  • cado-trigger-s3

    Trigger a disk acquisition and processing pipeline in Cado Response for a given file in an S3 bucket.

import demistomock as demisto  # noqa: F401
from CommonServerPython import *  # noqa: F401

""" Cado Response API Integration for the Cortex XSOAR Platform """

import time
import traceback
from typing import Any

import urllib3

from CommonServerUserPython import *

""" Module Level Declarations """

urllib3.disable_warnings()
CadoResponseCombinedOutput = Union[dict[str, Any], List[dict[str, Any]]]

DATE_FORMAT: str = "%Y-%m-%dT%H:%M:%SZ"

""" Cado Response API Client Code """


class Client(BaseClient):
    """Client that makes HTTP requests to the Cado Response API"""

    def heartbeat(self) -> dict[str, Any]:
        """Calls the GET /api/v2/system/status endpoint to verify
        everything is working

        :return JSON response from /system/status endpoint
        :rtype Dict[str, Any]
        """

        return self._http_request(method="GET", url_suffix="/system/status")

    def create_project(self, project_name: str, project_description: str | None) -> dict[str, Any]:
        """Calls the POST /api/v2/projects endpoint to create a new
        project with given parameters

        :param str project_name: Name of the project
        :param Optional[str] project_description: Description for the project

        :return JSON response from /projects endpoint
        :rtype Dict[str, Any]
        """

        if not project_name.endswith("_XSOAR"):
            project_name += "_XSOAR"

        if not project_description:
            project_description = "This is a project in Cado Response created through Cortex XSOAR!"

        payload: dict[str, Any] = {"caseName": project_name, "description": project_description}

        return self._http_request(method="POST", url_suffix="/projects", json_data=payload)

    def get_project(self, project_id: int | None) -> dict[str, Any]:
        """Calls the GET /api/v2/projects endpoint to retrieve a
        project with given parameters

        :param Optional[int] project_id: ID of the project to retrieve

        :return JSON response from /projects endpoint
        :rtype Dict[str, Any]
        """

        if not project_id:
            project_id = demisto.params().get("CadoResponse_DefaultProject", 1)

        return self._http_request(method="GET", url_suffix=f"/projects/{project_id}")

    def list_projects(self, limit: int) -> List[dict[str, Any]]:
        """Calls the GET /api/v2/projects endpoint to retrieve a list
        of created projects

        :return JSON response from /projects endpoint
        :rtype Dict[str, Any]
        """

        data: List[dict[str, Any]] = self._http_request(method="GET", url_suffix="/projects")

        return data[:limit]

    def get_pipeline(self, pipeline_id: int | None, project_id: int | None) -> dict[str, Any]:
        """Calls the GET /api/v2/tasks/pipelines endpoint to
        retrieve details about a given pipeline

        :param Optional[int] pipeline_id: The id of the pipeline to retrieve
        :param Optional[int] project_id: The id of the project the pipeline belongs to

        :return JSON response from /tasks/pipelines endpoint
        :rtype Dict[str, Any]
        """

        if not pipeline_id:
            return {}

        if not project_id:
            project_id = demisto.params().get("CadoResponse_DefaultProject", 1)

        return self._http_request(
            method="GET", url_suffix="/tasks/pipelines", params={"project_id": project_id, "pipeline_id": pipeline_id}
        )

    def list_pipelines(self, project_id: int | None, limit: int) -> List[dict[str, Any]]:
        """Calls the GET /api/v2/tasks/pipelines endpoint to
        retrieve details about all of a projects pipelines

        :param Optional[int] project_id: The id of the project the pipeline belongs to

        :return JSON response from /tasks/pipelines endpoint
        :rtype Dict[str, Any]
        """

        if not project_id:
            project_id = demisto.params().get("CadoResponse_DefaultProject", 1)

        data: dict[str, Any] = self._http_request(
            method="GET",
            url_suffix="/tasks/pipelines",
            params={
                "project_id": project_id,
            },
        )

        pipelines: List[dict[str, Any]] = data["pipelines"]

        return pipelines[:limit]

    def list_instances(self, project_id: int | None, region: str | None, limit: int) -> List[dict[str, Any]]:
        """Calls the GET /api/v2/projects/{id}/imports/ec2 endpoint to
        retrieve details about a regions EC2 instances

        :param Optional[int] project_id: The id of the project to query available instances in
        :param Optional[str] region: The AWS region to search instances in

        :return JSON response from /projects/{id}/imports/ec2 endpoint
        :rtype Dict[str, Any]
        """

        if not project_id:
            project_id = demisto.params().get("CadoResponse_DefaultProject", 1)

        if not region:
            region = demisto.params().get("CadoResponse_DefaultRegion", "us-east-1")

        data: dict[str, Any] = self._http_request(
            method="GET", url_suffix=f"/projects/{project_id}/imports/ec2", params={"region": region}
        )

        instances: List[dict[str, Any]] = data["instances"]

        return instances[:limit]

    def list_buckets(self, project_id: int | None, limit: int) -> dict[str, Any]:
        """Calls the GET /api/v2/projects/{id}/imports/s3 endpoint to
        retrieve details about all the available S3 buckets

        :param Optional[int] project_id: The id of the project to query available buckets in

        :return JSON response from /projects/{id}/imports/s3 endpoint
        :rtype Dict[str, Any]
        """

        if not project_id:
            project_id = demisto.params().get("CadoResponse_DefaultProject", 1)

        data: dict[str, Any] = self._http_request(method="GET", url_suffix=f"/projects/{project_id}/imports/s3")

        data["buckets"] = data["buckets"][:limit]

        return data

    def trigger_instance_acquisition(
        self,
        project_id: int | None,
        instance_id: str | None,
        region: str | None,
        bucket: str | None,
        compress: bool = True,
        include_disks: bool = True,
        include_hash: bool = False,
        include_logs: bool = True,
        include_screenshot: bool = True,
    ) -> dict[str, Any]:
        """Calls the POST /api/v2/projects/{id}/imports/ec2 endpoint to
        trigger an acquisition of a given instance

        :param Optional[int] project_id: The ID of the project you wish to attach the acquisition to
        :param str instance_id: ID of the EC2 instance to acquire
        :param Optional[str] region: AWS region in which the EC2 instance is located
        :param Optional[str] bucket: S3 bucket where the uploaded disk image resides
        :param bool compress: Flag indicating if disk compression is enabled
        :param bool include_disks: Flag indicating if we include disk image in the acquisition
        :param bool include_hash: Flag indicating if we calculate the hash of the disk
        :param bool include_logs: Flag indicating if we include system logs in the acquisition
        :param bool include_screenshot: Flag indicating if we include a screenshot of the system in the acquisition

        :return JSON response from /projects/{id}/imports/ec2 endpoint
        :rtype Dict[str, Any]
        """

        if not project_id:
            project_id = demisto.params().get("CadoResponse_DefaultProject", 1)

        if not region:
            region = demisto.params().get("CadoResponse_DefaultRegion", "us-east-1")

        if not bucket:
            bucket = demisto.params().get("CadoResponse_DefaultBucket", "cado-default-bucket")

        payload: dict[str, Any] = {
            "bucket": bucket,
            "compress": compress,
            "include_disks": include_disks,
            "include_hash": include_hash,
            "include_logs": include_logs,
            "include_screenshot": include_screenshot,
            "instance_id": instance_id,
            "region": region,
        }

        return self._http_request(method="POST", url_suffix=f"/projects/{project_id}/imports/ec2", json_data=payload)

    def trigger_bucket_acquisition(self, project_id: int | None, bucket: str | None, file_name: str | None) -> dict[str, Any]:
        """Calls the POST /api/v2/projects/{id}/imports/s3 endpoint to
        trigger an acquisition of a given bucket or file

        :param Optional[int] project_id: The ID of the project you wish to attach the acquisition to
        :param Optional[str] bucket: The S3 bucket name containing the file
        :param str file_name: The name of the file to process

        :return JSON response from /projects/{id}/imports/ec2 endpoint
        :rtype Dict[str, Any]
        """

        if not project_id:
            project_id = demisto.params().get("CadoResponse_DefaultProject", 1)

        if not bucket:
            bucket = demisto.params().get("CadoResponse_DefaultBucket", "cado-default-bucket")

        payload: dict[str, Any] = {"bucket": bucket, "file_name": file_name}

        return self._http_request(method="POST", url_suffix=f"/projects/{project_id}/imports/s3", json_data=payload)


""" Command Line Handlers """


def test_module(client: Client) -> str:
    """Command handler for !test-module"""

    result: dict[str, Any] = client.heartbeat()
    status: str | None = result["status"]

    if status is not None and status == "Running":
        return "ok"

    return "Cado Response is not running"


def create_project_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """Command handler for cado-create-project"""

    unix_timestamp: str = str(int(time.time()))
    project_name: str = args.get("project_name", unix_timestamp)
    project_description: str | None = args.get("project_description", None)
    result: dict[str, Any] = client.create_project(project_name, project_description)

    return CommandResults(outputs_prefix="CadoResponse.Project", outputs_key_field="id", outputs=result)


def list_project_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """Command handler for cado-list-project"""

    project_id: int | None = args.get("project_id", None)
    limit: int = int(args.get("limit", 50))

    if project_id:
        result: Any = client.get_project(project_id)
    else:
        result = client.list_projects(limit)

    return CommandResults(outputs_prefix="CadoResponse.Projects", outputs_key_field="id", outputs=result)


def get_pipeline_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """Command handler for cado-get-pipeline"""

    project_id: int | None = args.get("project_id", None)
    limit: int = int(args.get("limit", 50))
    pipeline_id: int | None = args.get("pipeline_id", None)

    if pipeline_id:
        result: CadoResponseCombinedOutput = client.get_pipeline(pipeline_id, project_id)
    else:
        result = client.list_pipelines(project_id, limit)

    return CommandResults(outputs_prefix="CadoResponse.Pipelines", outputs_key_field="pipeline_id", outputs=result)


def list_ec2_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """Command handler for cado-list-ec2"""

    project_id: int | None = args.get("project_id", None)
    region: str | None = args.get("region", None)
    limit: int = int(args.get("limit", 100))
    result: List[dict[str, Any]] = client.list_instances(project_id, region, limit)

    return CommandResults(outputs_prefix="CadoResponse.EC2Instances", outputs_key_field="id", outputs=result)


def list_s3_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """Command handler for cado-list-s3"""

    project_id: int | None = args.get("project_id", None)
    limit: int = int(args.get("limit", 100))
    result: dict[str, Any] = client.list_buckets(project_id, limit)

    return CommandResults(outputs_prefix="CadoResponse.S3Buckets", outputs=result)


def trigger_ec2_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """Command handler for cado-trigger-ec2"""

    project_id: int | None = args.get("project_id", None)
    instance_id: str | None = args.get("instance_id", None)
    region: str | None = args.get("region", None)
    bucket: str | None = args.get("bucket", None)
    compress: bool = args.get("compress", True)
    include_disks: bool = args.get("include_disks", True)
    include_hash: bool = args.get("include_hash", False)
    include_logs: bool = args.get("include_logs", True)
    include_screenshot: bool = args.get("include_screenshot", True)

    if not instance_id:
        raise DemistoException("region is a required parameter!")

    result: dict[str, Any] = client.trigger_instance_acquisition(
        project_id, instance_id, region, bucket, compress, include_disks, include_hash, include_logs, include_screenshot
    )

    return CommandResults(outputs_prefix="CadoResponse.EC2Acquistion", outputs_key_field="pipeline_id", outputs=result)


def trigger_s3_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """Command handler for cado-trigger-s3"""

    project_id: int | None = args.get("project_id", None)
    bucket: str | None = args.get("bucket", None)
    file_name: str | None = args.get("file_name", None)

    if not bucket:
        raise DemistoException("bucket is a required parameter!")

    if not file_name:
        raise DemistoException("file_name is a required parameter!")

    result: dict[str, Any] = client.trigger_bucket_acquisition(project_id, bucket, file_name)

    return CommandResults(
        outputs_prefix="CadoResponse.S3Acquisition", outputs_key_field="pipeline_id", outputs=result.get("pipelines")
    )


""" Helper Functions """


def enrich_errors(message: str, command: str) -> str:
    """Helper function to return better error messages.

    :param str message: Error message
    :param str command: Calling command

    :return: A better error message
    :rtype str
    """

    if command == "cado-create-project" and "Project name already exists" in message:
        return f'Project name {demisto.args().get("project_name")} already exists!'
    else:
        return f"Failed to execute {demisto.command()} command.\nError:\n{message}"


""" Entrypoint """


def main() -> None:
    api_key: str = demisto.params().get("apikey")
    base_url: str = urljoin(demisto.params()["url"], "/api/v2")
    verify_certificate: bool = not demisto.params().get("insecure", False)
    proxy: bool = demisto.params().get("proxy", False)
    command: str = demisto.command()
    args: dict[str, Any] = demisto.args()
    headers: dict[str, Any] = {"Authorization": f"Bearer {api_key}"}

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

        if command == "test-module":
            return_results(test_module(client))
        elif command == "cado-create-project":
            return_results(create_project_command(client, args))
        elif command == "cado-list-project":
            return_results(list_project_command(client, args))
        elif command == "cado-get-pipeline":
            return_results(get_pipeline_command(client, args))
        elif command == "cado-list-ec2":
            return_results(list_ec2_command(client, args))
        elif command == "cado-list-s3":
            return_results(list_s3_command(client, args))
        elif command == "cado-trigger-ec2":
            return_results(trigger_ec2_command(client, args))
        elif command == "cado-trigger-s3":
            return_results(trigger_s3_command(client, args))

    except Exception as e:
        message: str = str(e)

        if "404" in message:
            return_results(f"Nothing found for {command}")
        else:
            demisto.error(traceback.format_exc())
            return_error(enrich_errors(message, command), error=e)


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