TaegisXDR Deprecated

Deprecated. Use TaegisXDR v2 instead.

Data Enrichment & Threat Intelligence · Secureworks

Details

IDTaegisXDR
ProviderSophos
CategoryData Enrichment & Threat Intelligence
From Version5.5.0
Docker Imagedemisto/python3:3.10.12.65389
Supported ModulesAgentix XSIAM

README

Configure Taegis XDR in Cortex

Parameter Description Required
Taegis Environment The environment to utilize True
Client ID Client ID as described in the Taegis Documentation True
Client Secret Client Secret as described in the Taegis Documentation True
Use system proxy settings Defines whether the system proxy is used or not 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.

taegis-archive-investigation

Base Command

!taegis-archive-investigation

Inputs

Argument Name Description Required
id The investigation id to archive True

Command Example

!taegis-archive-investigation id=c207ca4c-8a78-4408-a056-49f05d6eb77d

Context Example

{
    "TaegisXDR": {
        "ArchivedInvestigation": {
            "id": "c207ca4c-8a78-4408-a056-49f05d6eb77d"
        }
    }
}

taegis-create-comment

Base Command

!taegis-create-comment

Inputs

Argument Name Description Required
comment The comment string to add to the investigation True
parent_id The investigation ID to add the comment to True

Command Example

!taegis-create-comment comment="This is a test comment" parent_id="219da0ee-8642-4363-827c-8a6fbd479082"

Context Example

{
    "TaegisXDR": {
        "CommentCreate": {
            "id": "593fa115-abad-4a52-9fc4-2ec403a8a1e4"
        }
    }
}

taegis-create-investigation

Base Command

!taegis-create-investigation

Inputs

Argument Name Description Required
description The subject or description of the investigation True
priority The priority for the investigiation [Default: 3] False

Command Example

!taegis-create-investigation priority=1 description="XSOAR Created Investigation"

Context Example

{
    "TaegisXDR": {
        "Investigation": {
            "id": "593fa115-abad-4a52-9fc4-2ec403a8a1e4"
        }
    }
}

taegis-execute-playbook

Base Command

!taegis-execute-playbook

Inputs

Argument Name Description Required
id Playbook instance ID to execute True
inputs JSON object of inputs to pass into the playbook execution False

Command Example

!taegis-execute-playbook id=UGxheWJvb2tJbnN0YW5jZTphZDNmNzBlZi1mN2U0LTQ0OWYtODJiMi1hYWQwMjQzZTA2NTg=
!taegis-execute-playbook id=UGxheWJvb2tJbnN0YW5jZTphZDNmNzBlZi1mN2U0LTQ0OWYtODJiMi1hYWQwMjQzZTA2NTg= inputs=`{'myvar': 'myval'}`

Context Example

{
    "id": "UGxheWJvb2tFeGVjdXRpb246NGYwZDZiNGQtNWNiZS00NDkxLTg3YzYtMDZkNjkxYzMwMTg4"
}

taegis-fetch-alerts

Base Command

!taegis-fetch-alerts

Input

Argument Name Description Default Required
ids A list of alerts by IDs 936c1cc1-db8f-430c-837c-1c914fcca35a False
limit Number of results to when ids is not defined 10 False
offset The result to start from when ids is not defined 0 False
cql_query The query to utilize when searching for Alerts from alert severity >= 0.6 and status='OPEN' False

Command Examples

!taegis-fetch-alerts ids=`["6594e97f-a898-5b28-82b2-ea03293cdaa1"]`

Context Example

{
    "TaegisXDR": {
        "Alerts": [
            {
                "id": "c4f33b53-eaba-47ac-8272-199af0f7935b",
                "metadata": {
                    "title": "Test Alert",
                    "description": "This is a test alert",
                    "severity": 0.5,
                }
            }
        ]
    }
}

taegis-fetch-assets

Base Command

!taegis-fetch-assets

Input

Argument Name Description Default Required
page   0 False
page_size   10 False
endpoint_type     False
host_id ID of the asset to fetch e43b545a-580a-4047-b489-4338c1cc4ba1 False
hostname     False
investigation_id     False
ip_address     False
mac_address     False
os_family     False
os_version     False
sensor_version     False
username     False

Command Examples

!taegis-fetch-assets
!taegis-fetch-assets page=1 page_size=5
!taegis-fetch-assets hostname=MyHostname01
!taegis-fetch-assets host_id=e43b545a-580a-4047-b489-4338c1cc4ba1

Context Example

{
    "TaegisXDR": {
        "Assets": [
            {
              "id": "",
              "ingestTime": "",
              "createdAt": "",
              "updatedAt": "",
              "deletedAt": "",
              "biosSerial": "",
              "firstDiskSerial": "",
              "systemVolumeSerial": "",
              "sensorVersion": "",
              "endpointPlatform": "",
              "hostnames": [{"id": ", "hostname": ""],
              "architecture": "",
              "osFamily": "",
              "osVersion": "",
              "osDistributor": "",
              "osRelease": "",
              "systemType": "",
              "osCodename": "",
              "kernelRelease": "",
              "kernelVersion": "",
              "tags": [ "key": "", "tag": ""],
              "endpointType": "",
              "hostId": "",
              "sensorId": "",
            }
        ]
    }
}

taegis-fetch-comment

Base Command

!taegis-fetch-comment

Inputs

Argument Name Description Required
id The ID of the comment to fetch True

Command Example

!taegis-fetch-comment id=ff9ca818-4749-4ccb-883a-2ccc6f6c9e0f

Context Example

{
    "TaegisXDR": {
        "Comment": {
            "author_user": {
                "email_normalized": "myuser@email.com",
                "given_name": "John",
                "family_name": "Smith",
                "id": "auth0|000000000000000000000001",
            },
            "id": "ff9ca818-4749-4ccb-883a-2ccc6f6c9e0f",
            "comment": "This is a comment in an investigation",
            "created_at": "2022-01-01T13:04:57.17234Z",
            "deleted_at": None,
            "modified_at": None,
            "parent_id": "c2e09554-833e-41a1-bc9d-8160aec0d70d",
            "parent_type": "investigation",
        }
    }
}

taegis-fetch-comments

Base Command

!taegis-create-comments

Inputs

Argument Name Description Required
parent_id The investigation ID to fetch comments for True

Command Example

!taegis-fetch-comments parent_id=c2e09554-833e-41a1-bc9d-8160aec0d70d

Context Example

{
    "TaegisXDR": {
        "Comments": [
            {
                "author_user": {
                    "email_normalized": "myuser@email.com",
                    "given_name": "John",
                    "family_name": "Smith",
                    "id": "auth0|000000000000000000000001",
                },
                "id": "ff9ca818-4749-4ccb-883a-2ccc6f6c9e0f",
                "comment": "This is a comment in an investigation",
                "created_at": "2022-01-01T13:04:57.17234Z",
                "deleted_at": None,
                "modified_at": None,
                "parent_id": "c2e09554-833e-41a1-bc9d-8160aec0d70d",
                "parent_type": "investigation",
            },
            {
                "author_user": {
                    "email_normalized": "myuser@email.com",
                    "given_name": "John",
                    "family_name": "Smith",
                    "id": "auth0|000000000000000000000001",
                },
                "id": "ff9ca818-4749-4ccb-883a-2ccc6f6c1234",
                "comment": "This is another comment",
                "created_at": "2022-01-02T13:04:57.17234Z",
                "deleted_at": None,
                "modified_at": None,
                "parent_id": "c2e09554-833e-41a1-bc9d-8160aec0d70d",
                "parent_type": "investigation",
            }
        ]
    }
}

taegis-fetch-endpoint

Base Command

!taegis-fetch-endpoint

Inputs

Argument Name Description Required
id Endpoint ID to fetch True

Command Example

!taegis-fetch-endpoint id=ff9ca818-4749-4ccb-883a-2ccc6f6c9e0f

Context Example

{
    "TaegisXDR": {
        "assetEndpointInfo": {
            "hostId": "",
            "hostName": "",
            "actualIsolationStatus": "",
            "allowedDomain": "",
            "desiredIsolationStatus": "",
            "firstConnectTime": "",
            "moduleHealth": {
                "enabled": ""
                "lastRunningTime": "",
                "moduleDisplayName": "",
            }
            "lastConnectAddress": "",
            "lastConnectTime": "",
            "sensorVersion": ""
        }
    }
}

taegis-fetch-investigation

Base Command

!taegis-fetch-investigation

Inputs

Argument Name Description Required
id Investigation ID to lookup True

Command Example

!taegis-fetch-investigation id=936c1cc1-db8f-430c-837c-1c914fcca35a

Context Example

{
    "TaegisXDR": {
        "Investigations": [
            {
                "archived_at": None,
                "created_at": "2022-02-02T13:53:35Z",
                "description": "Test Investigation",
                "id": "c2e09554-833e-41a1-bc9d-8160aec0d70d",
                "key_findings": "",
                "priority": 2,
                "service_desk_id": "",
                "service_desk_type": "",
                "status": "Open",
                "alerts2": [],
                "url": "https://ctpx.secureworks.com/investigations/c2e09554-833e-41a1-bc9d-8160aec0d70d",
            }
        ]
    }
}

taegis-fetch-investigation-alerts

Base Command

!taegis-fetch-investigation-alerts

Inputs

Argument Name Description Required
id Investigation ID to lookup True

Command Example

!taegis-fetch-investigation-alerts id=936c1cc1-db8f-430c-837c-1c914fcca35a

Context Example

{
    "TaegisXDR": {
        "InvestigationAlerts": [
            {
                "id": "c4f33b53-eaba-47ac-8272-199af0f7935b",
                "description": "Test Alert",
                "message": "This is a test alert",
                "severity": 0.5,
            }
        ]
    }
}

taegis-fetch-investigations

Base Command

!taegis-fetch-investigations

Inputs

Argument Name Description Required
page   False
page_size   False

Command Example

!taegis-fetch-investigations

Context Example

{
    "TaegisXDR": {
        "Investigations": [
            {
                "description": "Test Investigation",
                "id": "c2e09554-833e-41a1-bc9d-8160aec0d70d",
                "key_findings": "",
                "priority": 2,
                "service_desk_id": "",
                "service_desk_type": "",
                "status": "Open"
            }
        ]
    }
}

taegis-fetch-playbook-execution

Base Command

!taegis-fetch-playbook-execution

Inputs

Argument Name Description Required
id Playbook execution ID to fetch True

Command Example

!taegis-fetch-playbook-execution id=UGxheWJvb2tFeGVjdXRpb246NGYwZDZiNGQtNWNiZS00NDkxLTg3YzYtMDZkNjkxYzMwMTg4

Context Example

{
    "TaegisXDR": {
        "PlaybookExecution": {
            "createdAt": "2022-01-01T13:51:24Z",
            "executionTime": 1442,
            "id": "UGxheWJvb2tFeGVjdXRpb246NGYwZDZiNGQtNWNiZS00NDkxLTg3YzYtMDZkNjkxYzMwMTg4",
            "inputs": {
                "alert": {
                    "message": "Test Alert",
                }
            },
            "instance": {
                "name": "Test Alert Instance",
                "playbook": {
                    "name": "Taegis.PagerDutyAlertEvent"
                }
            },
            "outputs": "d6b65662-c1da-4109-8553-c5664918c952",
            "state": "Completed",
            "updatedAt": "2022-01-01T13:51:31Z"
        }
    }
}

taegis-fetch-users

Base Command

!taegis-fetch-users

Inputs

Argument Name Description Required
id The id of the user, in auth0 format False
email The email of the user False
status The users to find based on status False
page   False
page_size   False

Command Example

!taegis-fetch-users id="auth0|123456"

Context Example

{
    "TaegisXDR": {
        "Users": [
            {
                "email": "myuser@email.com",
                "family_name": "Smith",
                "given_name": "John",
                "status": "Registered",
                "user_id": "auth0|123456"
            }
        ]
    }
}

taegis-isolate-asset

Base Command

!taegis-isolate-asset

Input

Argument Name Description Default Required
id ID of the asset to isolate e43b545a-580a-4047-b489-4338c1cc4ba1 True
reason The reason for the isolation See ticket 12345 True

Command Examples

!taegis-isolate-asset id="e43b545a-580a-4047-b489-4338c1cc4ba1" reason="See ticket 12345"

Context Example

{
    "TaegisXDR": {
        "AssetIsolation": {
            "id": "e43b545a-580a-4047-b489-4338c1cc4ba1"
        }
    }
}

taegis-update-alert-status

Base Command

!taegis-update-alert-status

Input

Argument Name Description Default Required
ids A comma-separated list of alerts by IDs alert://priv:crowdstrike:11772:1666269058114:59284e28-4ec8-542b-a4a1-452c3688bc1a True
status The status to update the alert(s) with FALSE_POSITIVE True
reason A comment/reason for the alert status update See ticket 13245 False
Permitted Status Values
  • FALSE_POSITIVE
  • NOT_ACTIONABLE
  • OPEN
  • TRUE_POSITIVE_BENIGN
  • TRUE_POSITIVE_MALICIOUS

Command Examples

!taegis-update-alert-status ids="alert://priv:crowdstrike:11772:1677742145475:07e2d9cc-0a04-55ec-890a-97f39d63698e" status=NOT_ACTIONABLE reason="Test Reason"

Context Example

{
    "TaegisXDR": {
        "AlertStatusUpdate": {
            "reason": "feedback updates successfully applied",
            "resolution_status": "SUCCESS"
        }
    }
}

taegis-update-comment

Base Command

!taegis-update-comment

Inputs

Argument Name Description Required
comment The comment string to add to the investigation True
id The comment ID to update True

Command Example

!taegis-update-comment id="ff9ca818-4749-4ccb-883a-2ccc6f6c9e0f" comment="Newly updated comment"

Context Example

{
    "TaegisXDR": {
        "CommentUpdate": {
            "id": "593fa115-abad-4a52-9fc4-2ec403a8a1e4"
        }
    }
}

taegis-update-investigation

Base Command

!taegis-update-investigation

Inputs

Argument Name Description Required
id Investigation ID to update True
key_findings   False
prioirity The priority of the Investigation (1-5) False
service_desk_id An ID or ticket # to relate to an Investigation False
service_desk_type The type of id related to an investigation (e.g. Jira) False
status The current status of the Investigation False
assignee_id The id of a user to assign, in auth0|12345 format False

Note: At least 1 of the above inputs (in addition to id) must be defined

Permitted Status Values
  • Active
  • Awaiting Action
  • Closed: Authorized Activity
  • Closed: Confirmed Security Incident
  • Closed: False Positive Alert
  • Closed: Inconclusive
  • Closed: Informational
  • Closed: Not Vulnerable
  • Closed: Threat Mitigated
  • Open
  • Suspended

Command Example

!taegis-update-investigation id="936c1cc1-db8f-430c-837c-1c914fcca35a" priority=3 status="Open" service_desk_id="XDR-1234" service_desk_type="Jira"

Context Example

{
    "TaegisXDR": {
        "InvestigationUpdate": {
            "id": "c2e09554-833e-41a1-bc9d-8160aec0d70d"
        }
    }
}

taegis-unarchive-investigation

Base Command

!taegis-unarchive-investigation

Inputs

Argument Name Description Required
id The investigation id to unarchive True

Command Example

!taegis-unarchive-investigation id=c207ca4c-8a78-4408-a056-49f05d6eb77d

Context Example

{
    "TaegisXDR": {
        "UnarchivedInvestigation": {
            "id": "c207ca4c-8a78-4408-a056-49f05d6eb77d"
        }
    }
}

Configuration parameters

  • environment — Taegis Environment (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • client_id — Client ID (required)
  • client_secret — Client Secret (required)
  • isFetch — Fetch incidents
  • max_fetch — Maximum number of incidents per fetch
  • first_fetch — First fetch time interval
  • incidentFetchInterval — Incidents Fetch Interval
  • incidentType — Incident type
  • include_assets — Include Assets in Fetch

Commands (18)

  • taegis-archive-investigation

    Archive an investigation

  • taegis-create-comment

    Create a comment on an investigation

  • taegis-create-investigation

    Create an Investigation within Taegis

  • taegis-execute-playbook

    Executes a Taegis playbook instance

  • taegis-fetch-alerts

    List Taegis alerts by ID

  • taegis-fetch-assets

    Fetch assets based on search criteria

  • taegis-fetch-comment

    Fetch comment by comment ID

  • taegis-fetch-comments

    Fetch comments by Parent Type and ID

  • taegis-fetch-endpoint

    Fetch endpoint information

  • taegis-fetch-investigation

    Fetch all investigations or a specific investigation

  • taegis-fetch-investigation-alerts

    Fetch Alert IDs related to a specific investigation

  • taegis-fetch-playbook-execution

    Fetch the results of a Taegis playbook instance execution

  • taegis-fetch-users

    Fetch a user by ID or email address

  • taegis-isolate-asset

    Isolate a specific asset

  • taegis-unarchive-investigation

    Unarchive an investigation

  • taegis-update-alert-status

    Update the status of an alert

  • taegis-update-comment

    Update an existing comment

  • taegis-update-investigation

    Update an existing investigation

import demistomock as demisto  # noqa: F401
from CommonServerPython import *  # noqa: F401
from typing import Any, Dict


""" CONSTANTS """


AUTH_ENDPOINT = "/auth/api/v2/auth/token"
GRAPHQL_ENDPOINT = "/graphql"

ENV_URLS = {
    "us1": {"api": "https://api.ctpx.secureworks.com", "xdr": "https://ctpx.secureworks.com"},
    "us2": {"api": "https://api.delta.taegis.secureworks.com", "xdr": "https://delta.taegis.secureworks.com"},
    "eu": {"api": "https://api.echo.taegis.secureworks.com", "xdr": "https://echo.taegis.secureworks.com"},
}

ALERT_STATUSES = set((
    "FALSE_POSITIVE",
    "NOT_ACTIONABLE",
    "OPEN",
    "TRUE_POSITIVE_BENIGN",
    "TRUE_POSITIVE_MALICIOUS",
))
ASSET_SEARCH_FIELDS = ((
    "endpoint_type",
    "host_id",
    "hostname",
    "investigation_id",
    "ip_address",
    "mac_address",
    "os_family",
    "os_version",
    "sensor_version",
    "username",
))
COMMENT_TYPES = set((
    "investigation",
))
INVESTIGATION_STATUSES = set((
    "Open",
    "Active",
    "Awaiting Action",
    "Suspended",
    "Closed: Authorized Activity",
    "Closed: Confirmed Security Incident",
    "Closed: False Positive Alert",
    "Closed: Inconclusive",
    "Closed: Informational",
    "Closed: Not Vulnerable",
    "Closed: Threat Mitigated",
))
INVESTIGATION_UPDATE_FIELDS = set(("key_findings", "priority", "status", "service_desk_id", "service_desk_type", "assignee_id"))


""" CLIENT """


class Client(BaseClient):
    """
    Secureworks Taegis XDR Client class for implementing API logic with Taegis
    """
    _auth_header = {"access_token": "None"}

    def __init__(
        self,
        client_id: str,
        client_secret: str,
        base_url: str,
        proxy: bool = False,
        verify: bool = True,
    ) -> None:
        super().__init__(base_url=base_url, verify=verify, proxy=proxy)
        self.base_url = base_url
        self._client_id = client_id
        self._client_secret = client_secret
        self.verify = verify
        return

    def auth(self) -> None:
        """Authenticate to the Taegis API using client_id and client_secret

        See the documentation for obtaining the client ID and secret: https://docs.ctpx.secureworks.com/apis/api_authenticate/
        """
        response = self._http_request(
            "POST",
            AUTH_ENDPOINT,
            headers={"Content-Type": "application/json"},
            json_data={"grant_type": "client_credentials"},
            auth=(self._client_id, self._client_secret),
        )

        token = response.get("access_token", None)
        self._auth_header = {"Authorization": f"Bearer {token}"}

        return

    def graphql_run(self, query: str, variables: Dict[str, Any] = None):
        """Perform a GraphQL query

        :type query: ``str``
        :param query: The GraphQL query

        :type variables: ``Dict[str, Any]``
        :param variables: The variables to utilize with the query
        """
        json_data: Dict[str, Any] = {"query": query}

        if variables:
            json_data["variables"] = variables

        response = self._http_request(
            method="POST",
            url_suffix=GRAPHQL_ENDPOINT,
            json_data=json_data,
            headers=self._auth_header,
        )
        return response

    def test(self) -> Dict[str, Any]:
        """
        Get the current API/asset version for testing auth and connectivity
        """
        response = self._http_request(
            method="GET",
            url_suffix="/assets/version",
            headers=self._auth_header,
        )
        return response


""" COMMANDS """


def create_comment_command(client: Client, env: str, args=None):
    if not args.get("comment"):
        raise ValueError("Cannot create comment, comment cannot be empty")

    if not args.get("parent_id"):
        raise ValueError("Cannot create comment, parent_id cannot be empty")

    parent_type = args.get("parent_type", "investigation").lower()
    if parent_type not in COMMENT_TYPES:
        raise ValueError(
            f"The provided comment parent type, {parent_type}, is not valid. "
            f"Supported Parent Types Values: {COMMENT_TYPES}"
        )

    query = """
    mutation createComment ($comment: CommentInput!) {
        createComment(comment: $comment) {
            id
        }
    }
    """

    variables = {
        "comment": {
            "comment": args.get("comment"),
            "parent_id": args.get("parent_id"),
            "parent_type": parent_type,
            "section_id": args.get("section_id", ""),
            "section_type": args.get("section_type", ""),
        }
    }

    result = client.graphql_run(query=query, variables=variables)

    try:
        comment = result["data"]["createComment"]
    except (KeyError, TypeError):
        raise ValueError(f"Failed to create comment: {result['errors'][0]['message']}")

    results = CommandResults(
        outputs_prefix="TaegisXDR.CommentCreate",
        outputs_key_field="id",
        outputs=comment,
        readable_output=tableToMarkdown(
            "Taegis Comment",
            comment,
            removeNull=True,
        ),
        raw_response=result,
    )

    return results


def create_investigation_command(client: Client, env: str, args=None):
    query = """
    mutation ($investigation: InvestigationInput!) {
    createInvestigation(investigation: $investigation) {
            id
        }
    }
    """

    variables = {
        "investigation": {
            "description": args.get("description", "Demisto Created Investigation"),
            "priority": args.get("priority", 2),
            "status": "Open",
        }
    }

    result = client.graphql_run(query=query, variables=variables)

    try:
        investigation = result["data"]["createInvestigation"]
        investigation["url"] = generate_id_url(env, "investigations", investigation["id"])
    except (KeyError, TypeError):
        raise ValueError(f"Failed to create investigation: {result['errors'][0]['message']}")

    results = CommandResults(
        outputs_prefix="TaegisXDR.Investigation",
        outputs_key_field="id",
        outputs=investigation,
        readable_output=tableToMarkdown(
            "Taegis Investigation",
            investigation,
            removeNull=True,
            url_keys=("url"),
        ),
        raw_response=result,
    )

    return results


def execute_playbook_command(client: Client, env: str, args=None):
    playbook_id = args.get("id")
    if not playbook_id:
        raise ValueError("Cannot execute playbook, missing playbook_id")

    query = """
    mutation executePlaybookInstance(
        $playbookInstanceId: ID!
        $parameters: JSONObject
    ) {
        executePlaybookInstance(
            playbookInstanceId: $playbookInstanceId
            parameters: $parameters
        ) {
            id
        }
    }
    """

    playbook_inputs = args.get("inputs", {})

    variables = {
        "playbookInstanceId": playbook_id,
        "parameters": playbook_inputs,
    }

    result = client.graphql_run(query=query, variables=variables)

    if not result.get("data"):
        raise ValueError(f"Failed to execute playbook: {result['errors'][0]['message']}")

    execution = result["data"]["executePlaybookInstance"]
    execution["url"] = generate_id_url(env, "automations/playbook-executions", execution["id"])

    results = CommandResults(
        outputs_prefix="TaegisXDR.Execution",
        outputs_key_field="id",
        outputs=execution,
        readable_output=tableToMarkdown(
            "Taegis Playbook Execution",
            execution,
            removeNull=True,
            url_keys=("url"),
        ),
        raw_response=result,
    )

    return results


def fetch_alerts_command(client: Client, env: str, args=None):
    """
    Fetch a specific alert or a list of alerts based on a CQL Taegis query
    """
    variables: dict = {
        "cql_query": args.get("cql_query", "from alert severity >= 0.6 and status='OPEN'"),
        "limit": args.get("limit", 10),
        "offset": args.get("offset", 0),
        "ids": args.get("ids", []),  # ["alerts://id1", "alerts://id2"]
    }
    fields: str = """
            status
            reason
            alerts {
                total_results
                list {
                    id
                    tenant_id
                    status
                    suppressed
                    suppression_rules {
                      id
                      version
                    }
                    resolution_reason
                    attack_technique_ids
                    entities{
                      entities
                      relationships{
                        from_entity
                        relationship
                        to_entity
                      }
                    }
                    metadata {
                      engine {
                        name
                      }
                      creator {
                        detector {
                          version
                          detector_id
                        }
                        rule {
                          rule_id
                          version
                        }
                      }
                      title
                      description
                      confidence
                      severity
                      created_at {
                        seconds
                      }
                    }
                    investigation_ids {
                      id
                    }
                    sensor_types
                }
            }
        """

    if args.get("ids"):
        field = "alertsServiceRetrieveAlertsById"
        query = """
        query alertsServiceRetrieveAlertsById($ids: [String!]) {
            alertsServiceRetrieveAlertsById(
                in: {
                    iDs: $ids
                }
            ) {
                %s
            }
        }
        """ % (fields)

        if type(variables["ids"]) == str:
            variables["ids"] = variables["ids"].split(",")  # alerts://id1,alerts://id2
        variables["ids"] = [x.strip() for x in variables["ids"]]  # Ensure no whitespace
    else:
        field = "alertsServiceSearch"
        query = """
        query alertsServiceSearch($cql_query: String, $limit: Int, $offset: Int) {
            alertsServiceSearch(
                in: {
                    cql_query:$cql_query,
                    offset:$offset,
                    limit:$limit
                }
            ) {
                %s
            }
        }
        """ % (fields)

    result = client.graphql_run(query=query, variables=variables)
    alerts = result["data"][field]["alerts"]["list"]

    for alert in alerts:
        alert.update({"url": generate_id_url(env, "alerts", alert["id"])})

    results = CommandResults(
        outputs_prefix="TaegisXDR.Alerts",
        outputs_key_field="id",
        outputs=alerts,
        readable_output=tableToMarkdown(
            "Taegis Alerts",
            alerts,
            removeNull=True,
            url_keys=("url"),
        ),
        raw_response=result,
    )

    return results


def fetch_assets_command(client: Client, env: str, args=None):
    page = arg_to_number(args.get("page")) or 0
    page_size = arg_to_number(args.get("page_size")) or 10

    variables: Dict[str, Any] = {
        "input": {},
        "pagination_input": {
            "limit": page_size,
            "offset": page_size * page,
        }
    }

    # Loop over allowed search fields and add valid search options to the query variables
    for field in ASSET_SEARCH_FIELDS:
        if args.get(field):
            variables["input"][field] = args.get(field).strip()

    query = """
    query searchAssetsV2($input: SearchAssetsInput!, $pagination_input: SearchAssetsPaginationInput!) {
         searchAssetsV2(input: $input, paginationInput:$pagination_input) {
           assets {
              id
              ingestTime
              createdAt
              updatedAt
              deletedAt
              biosSerial
              firstDiskSerial
              systemVolumeSerial
              sensorVersion
              endpointPlatform
              architecture
              osFamily
              osVersion
              osDistributor
              osRelease
              systemType
              osCodename
              kernelRelease
              kernelVersion
              hostnames {
                id
                hostname
              },
              tags {
                key
                tag
              }
              endpointType
              hostId
              sensorId
            }
          }
        }
    """

    result = client.graphql_run(query=query, variables=variables)
    try:
        assets = result["data"]["searchAssetsV2"]["assets"]
    except (KeyError, TypeError):
        raise ValueError(f"Failed to fetch assets: {result['errors'][0]['message']}")

    results = CommandResults(
        outputs_prefix="TaegisXDR.Assets",
        outputs_key_field="id",
        outputs=assets,
        readable_output=tableToMarkdown(
            "Taegis Assets",
            assets,
            removeNull=True,
        ),
        raw_response=result,
    )

    return results


def fetch_comment_command(client: Client, env: str, args=None):
    comment_id = args.get("id")
    if not comment_id:
        raise ValueError("Cannot fetch comment, missing comment_id")

    query = """
    query comment ($comment_id: ID!) {
        comment(comment_id: $comment_id) {
            author_user {
                id
                family_name
                given_name
                email_normalized
            }
            id
            comment
            modified_at
            deleted_at
            created_at
            parent_id
            parent_type
        }
    }
    """

    variables = {"comment_id": comment_id}

    result = client.graphql_run(query=query, variables=variables)

    try:
        comment = result["data"]["comment"]
    except (KeyError, TypeError):
        raise ValueError("Could not locate comment by provided ID")

    results = CommandResults(
        outputs_prefix="TaegisXDR.Comment",
        outputs_key_field="id",
        outputs=comment,
        readable_output=tableToMarkdown(
            "Taegis Comment",
            comment,
            removeNull=True,
        ),
        raw_response=result,
    )

    return results


def fetch_comments_command(client: Client, env: str, args=None):
    if not args.get("parent_id"):
        raise ValueError("Cannot fetch comments, missing parent_id")

    parent_type = args.get("parent_type", "investigation")
    if parent_type not in COMMENT_TYPES:
        raise ValueError((
            f"The provided comment parent type, {parent_type}, is not valid. "
            f"Supported Parent Types Values: {parent_type}"
        ))

    query = """
    query commentsByParent ($parent_type: String!, $parent_id: String!) {
        commentsByParent(parent_type: $parent_type,parent_id:$parent_id) {
            author_user {
                id
                family_name
                given_name
                email_normalized
            }
            id
            comment
            modified_at
            deleted_at
            created_at
            parent_id
            parent_type
        }
    }
    """

    variables = {
        "parent_id": args.get("parent_id"),
        "parent_type": parent_type
    }

    result = client.graphql_run(query=query, variables=variables)

    try:
        comments = result["data"]["commentsByParent"]
    except (KeyError, TypeError):
        raise ValueError(f"Failed to fetch comments: {result['errors'][0]['message']}")

    results = CommandResults(
        outputs_prefix="TaegisXDR.Comments",
        outputs_key_field="id",
        outputs=comments,
        readable_output=tableToMarkdown(
            "Taegis Comments",
            comments,
            removeNull=True,
        ),
        raw_response=result,
    )

    return results


def fetch_endpoint_command(client: Client, env: str, args=None):
    if not args.get("id"):
        raise ValueError("Cannot fetch endpoint information, missing id")

    variables: Dict[str, Any] = {
        "id": args.get("id")
    }

    query = """
    query assetEndpointInfo($id: ID!) {
      assetEndpointInfo(id: $id) {
        hostId
        hostName
        actualIsolationStatus
        allowedDomain
        desiredIsolationStatus
        firstConnectTime
        moduleHealth {
            enabled
            lastRunningTime
            moduleDisplayName
        }
        lastConnectAddress
        lastConnectTime
        sensorVersion
      }
    }
    """

    result = client.graphql_run(query=query, variables=variables)
    try:
        endpoint = result["data"]["assetEndpointInfo"]
    except (KeyError, TypeError):
        raise ValueError(f"Failed to fetch endpoint information: {result['errors'][0]['message']}")

    results = CommandResults(
        outputs_prefix="TaegisXDR.Endpoint",
        outputs_key_field="hostId",
        outputs=endpoint,
        readable_output=tableToMarkdown(
            "Taegis Endpoint",
            endpoint,
            removeNull=True,
        ),
        raw_response=result,
    )

    return results


def fetch_incidents(client: Client, max_fetch: int = 15, include_assets: bool = True):
    """
    Fetch Taegis Investigations for the use with "Fetch Incidents"
    """
    if not 0 < int(max_fetch) < 201:
        raise ValueError("Max Fetch must be between 1 and 200")

    asset_query = ""
    if include_assets:
        demisto.debug("include_assets=True, fetching assets with investigation")
        asset_query = "assets {id hostnames {id hostname} tags {tag}}"

    query = """
    query investigations(
          $page: Int,
          $perPage: Int,
          $status: [String],
          $createdAfter: String,
          $orderByField: OrderFieldInput,
          $orderDirection: OrderDirectionInput
      ) {
          allInvestigations(
              page: $page,
              perPage: $perPage,
              status: $status,
              createdAfter: $createdAfter,
              orderByField: $orderByField,
              orderDirection: $orderDirection
          ) {
            id
            tenant_id
            description
            key_findings
            assignee {
                name
                id
                email
            }
            alerts2 {
                id
                suppressed
                status
                priority {
                    value
                }
                metadata {
                    title
                    description
                    created_at {
                        seconds
                    }
                    severity
                    confidence
                }
            }
            archived_at
            created_at
            updated_at
            service_desk_id
            service_desk_type
            latest_activity
            priority
            status
            %s
        }
    }
    """ % (asset_query)

    variables = {
        "orderByField": "created_at",
        "orderDirection": "asc",
        "page": 0,
        "perPage": max_fetch,
        "status": ["Open", "Active", "Awaiting Action"]
    }

    last_run = demisto.getLastRun()
    demisto.debug(f"Last Fetch Incident Run: {last_run}")

    now = datetime.now()
    start_time = str(now - timedelta(days=1))  # Default start if first ever run
    if last_run and "start_time" in last_run:
        start_time = last_run.get("start_time")
    variables["createdAfter"] = start_time

    result = client.graphql_run(query=query, variables=variables)

    if result.get("errors") and result["errors"]:
        raise DemistoException(f"Error when fetching investigations: {result['errors'][0]['message']}")

    incidents = []
    for investigation in result["data"]["allInvestigations"]:
        # createdAfter really means createdAtOrAfter so skip the duplicate
        if start_time == investigation["created_at"]:
            continue

        # Skip archived, if necessary
        if investigation["archived_at"]:
            demisto.debug(f"Skipping Archived Investigation: {investigation['description']} ({investigation['id']})")
            continue

        demisto.debug(f"Found New Investigation: {investigation['description']} ({investigation['id']})")
        incidents.append({
            "name": investigation["description"],
            "occured": investigation["created_at"],
            "rawJSON": json.dumps(investigation)
        })

    demisto.debug(f"Located {len(incidents)} Incidents")

    last_run = str(now) if not incidents else incidents[-1]["occured"]
    demisto.debug(f"Last Run/Incident Time: {last_run}")
    demisto.setLastRun({"start_time": last_run})

    demisto.incidents(incidents)

    return incidents


def fetch_investigation_alerts_command(client: Client, env: str, args=None):
    investigation_id = args.get("id")
    page = args.get("page", 0)
    page_size = args.get("page_size", 10)
    if not investigation_id:
        raise ValueError("Cannot fetch investigation, missing investigation_id")

    query = """
    query investigationAlerts($investigation_id: ID!, $page: Int, $perPage: Int) {
        investigationAlerts(investigation_id: $investigation_id, page: $page, perPage: $perPage) {
            alerts {
                id
            }
            alerts2 {
                id
            }
            totalCount
        }
    }
    """

    variables = {"page": page, "perPage": page_size, "investigation_id": investigation_id}
    result = client.graphql_run(query=query, variables=variables)

    try:
        alerts = result["data"]["investigationAlerts"]["alerts"]
    except (KeyError, TypeError):
        alerts = []

    for alert in alerts:
        alert.update({"url": generate_id_url(env, "alerts", alert["id"])})

    results = CommandResults(
        outputs_prefix="TaegisXDR.InvestigationAlerts",
        outputs_key_field="id",
        outputs=alerts,
        readable_output=tableToMarkdown(
            "Taegis Investigation Alerts",
            alerts,
            removeNull=True,
            url_keys=("url"),
        ),
        raw_response=result,
    )

    return results


def fetch_investigation_command(client: Client, env: str, args=None):
    investigation_id = args.get("id", None)
    page = args.get("page", 0)
    page_size = args.get("page_size", 10)
    status = args.get("status", [])

    fields = """
        id
        tenant_id
        description
        key_findings
        alerts2 {
            id
            suppressed
            status
            priority {
                value
            }
            metadata {
                title
                description
                created_at {
                    seconds
                }
                severity
                confidence
            }
        }
        genesis_alerts2 {
            id
            suppressed
            status
            priority {
                value
            }
            metadata {
                title
                description
                created_at {
                    seconds
                }
                severity
                confidence
            }
        }
        assignee {
            name
            id
            email
        }
        archived_at
        created_at
        updated_at
        service_desk_id
        service_desk_type
        latest_activity
        priority
        status
        assets {
            id
            hostnames {
                id
                hostname
            }
            tags {
                tag
            }
        }
        """

    if investigation_id:
        query = """
        query investigation($investigation_id: ID!) {
            investigation(investigation_id: $investigation_id) {
                %s
            }
        }
        """ % (fields)

        variables = {"investigation_id": investigation_id}
        result = client.graphql_run(query=query, variables=variables)
    else:
        query = """
        query investigations($page: Int, $perPage: Int, $status: [String]) {
            allInvestigations(page: $page, perPage: $perPage, status: $status) {
                %s
            }
        }
        """ % (fields)
        variables = {"page": page, "perPage": page_size, "status": status}
        result = client.graphql_run(query=query, variables=variables)

    try:
        investigations = [result["data"]["investigation"]] if investigation_id else result["data"]["allInvestigations"]
    except (KeyError, TypeError):
        investigations = []

    for investigation in investigations:
        investigation.update({"url": generate_id_url(env, "investigations", investigation["id"])})

    results = CommandResults(
        outputs_prefix="TaegisXDR.Investigations",
        outputs_key_field="id",
        outputs=investigations,
        readable_output=tableToMarkdown(
            "Taegis Investigations",
            investigations,
            removeNull=True,
            url_keys=("url"),
        ),
        raw_response=result,
    )

    return results


def fetch_playbook_execution_command(client: Client, env: str, args=None):
    execution_id = args.get("id")
    if not execution_id:
        raise ValueError("Cannot fetch playbook execution, missing execution id")

    query = """
    query playbookExecution($playbookExecutionId: ID!) {
      playbookExecution(playbookExecutionId: $playbookExecutionId) {
        id
        state
        instance {
          name
          playbook {
              name
          }
        }
        inputs
        createdAt
        updatedAt
        executionTime
        outputs
      }
    }
    """

    variables = {
        "playbookExecutionId": execution_id
    }

    result = client.graphql_run(query=query, variables=variables)

    try:
        execution = result['data']["playbookExecution"]
        execution["url"] = generate_id_url(env, "automations/playbook-executions", execution["id"])
    except (KeyError, TypeError):
        raise ValueError(f"Failed to fetch playbook execution: {result['errors'][0]['message']}")

    results = CommandResults(
        outputs_prefix="TaegisXDR.PlaybookExecution",
        outputs_key_field="id",
        outputs=execution,
        readable_output=tableToMarkdown(
            "Taegis Playbook Execution",
            execution,
            removeNull=True,
            url_keys=("url"),
        ),
        raw_response=result,
    )

    return results


def fetch_users_command(client: Client, env: str, args=None):
    page = int(args.get("page", 0))
    page_size = int(args.get("page_size", 10))

    variables: Dict[str, Any] = {
        "filters": {
            "status": args.get("status", ""),
            "perPage": page_size,
            "pageOffset": page_size * page,
        }
    }
    fields = "user_id email family_name given_name status"
    if args.get("id"):
        if not args["id"].startswith("auth0"):
            raise ValueError("id MUST be in 'auth0|12345' format")

        query = """
        query ($ids: [String!]) {
            tdrusersByIDs (userIDs: $ids) {
                %s
            }
        }
        """ % (fields)
        variables = {"ids": [args["id"]]}
    else:
        query = """
        query ($filters: TDRUsersSearchInput) {
            tdrUsersSearch (filters: $filters) {
                results {
                    %s
                }
            }
        }
        """ % (fields)

    if args.get("email"):
        variables["filters"]["emails"] = args["email"]

    result = client.graphql_run(query=query, variables=variables)

    try:
        if args.get("id"):
            user = result["data"]["tdrusersByIDs"]
        else:
            user = result["data"]["tdrUsersSearch"]["results"]
    except (KeyError, TypeError):
        raise ValueError(f"Failed to fetch user information: {result['errors'][0]['message']}")

    results = CommandResults(
        outputs_prefix="TaegisXDR.Users",
        outputs_key_field="user_id",
        outputs=user,
        readable_output=tableToMarkdown(
            "Taegis Users",
            user,
            removeNull=True,
        ),
        raw_response=result,
    )

    return results


def isolate_asset_command(client: Client, env: str, args=None):
    if not args.get("id"):
        raise ValueError("Cannot isolate asset, missing id")
    if not args.get("reason"):
        raise ValueError("Cannot isolate asset, missing reason")

    variables: Dict[str, Any] = {
        "id": args.get("id"),
        "reason": args.get("reason")
    }

    query = """
    mutation isolateAsset ($id: ID!, $reason: String!) {
      isolateAsset (id: $id, reason: $reason) {
        id
      }
    }
    """

    result = client.graphql_run(query=query, variables=variables)

    try:
        isolation = result["data"]["isolateAsset"]
    except (KeyError, TypeError):
        raise ValueError(f"Failed to isolate asset: {result['errors'][0]['message']}")

    results = CommandResults(
        outputs_prefix="TaegisXDR.AssetIsolation",
        outputs_key_field="id",
        outputs=isolation,
        readable_output=tableToMarkdown(
            "Taegis Asset Isolation",
            isolation,
            removeNull=True,
        ),
        raw_response=result,
    )

    return results


def update_alert_status_command(client: Client, env: str, args=None):
    if not args.get("ids"):
        raise ValueError("Alert IDs must be defined")
    if not args.get("status"):
        raise ValueError("Alert status must be defined")

    if args.get("status").upper() not in ALERT_STATUSES:
        raise ValueError((
            f"The provided status, {args['status']}, is not valid for updating an alert. "
            f"Supported Status Values: {ALERT_STATUSES}"))

    variables = {
        "alert_ids": argToList(args.get("ids")),
        "reason": args.get("reason", ""),
        "resolution_status": args.get("status"),
    }

    query = """
    mutation alertsServiceUpdateResolutionInfo($alert_ids: [String!], $reason: String, $resolution_status: ResolutionStatus) {
      alertsServiceUpdateResolutionInfo(
        in: {
          alert_ids: $alert_ids,
            reason: $reason,
            resolution_status: $resolution_status
        }
      ) {
        resolution_status
        reason
      }
    }
    """
    result = client.graphql_run(query=query, variables=variables)

    try:
        update_result = result["data"]["alertsServiceUpdateResolutionInfo"]
    except (KeyError, TypeError):
        raise ValueError(f"Failed to locate/update alert: {result['errors'][0]['message']}")

    results = CommandResults(
        outputs_prefix="TaegisXDR.AlertStatusUpdate",
        outputs_key_field="status",
        outputs=update_result,
        readable_output=tableToMarkdown(
            "Taegis Alert Update",
            update_result,
            removeNull=True,
        ),
        raw_response=result,
    )

    return results


def update_comment_command(client: Client, env: str, args=None):
    if not args.get("id"):
        raise ValueError("Cannot update comment, comment id cannot be empty")

    if not args.get("comment"):
        raise ValueError("Cannot update comment, comment cannot be empty")

    query = """
    mutation updateComment ($comment_id: ID!, $comment: CommentUpdate!) {
        updateComment(comment_id: $comment_id, comment: $comment) {
            id
        }
    }
    """
    variables = {
        "comment_id": args.get("id"),
        "comment": {
            "comment": args.get("comment")
        },
    }

    result = client.graphql_run(query=query, variables=variables)

    try:
        comment = result["data"]["updateComment"]
    except (KeyError, TypeError):
        raise ValueError(f"Failed to locate/update comment: {result['errors'][0]['message']}")

    results = CommandResults(
        outputs_prefix="TaegisXDR.CommentUpdate",
        outputs_key_field="id",
        outputs=comment,
        readable_output=tableToMarkdown(
            "Taegis Comment",
            comment,
            removeNull=True,
        ),
        raw_response=result,
    )

    return results


def update_investigation_command(client: Client, env: str, args=None):
    investigation_id = args.get("id")
    if not investigation_id:
        raise ValueError("Cannot fetch investigation without investigation_id defined")

    if args.get("assignee_id"):
        if not args["assignee_id"].startswith("auth0") and args["assignee_id"] != "@secureworks":
            raise ValueError("assignee_id MUST be in 'auth0|12345' format or '@secureworks'")

    query = """
    mutation ($investigation_id: ID!, $investigation: UpdateInvestigationInput!) {
        updateInvestigation(investigation_id: $investigation_id, investigation: $investigation) {
            id
        }
    }
    """
    variables = {"investigation_id": investigation_id, "investigation": dict()}

    for field in INVESTIGATION_UPDATE_FIELDS:
        if not args.get(field):
            continue

        if field == "status" and args.get("status") not in INVESTIGATION_STATUSES:
            raise ValueError((
                f"The provided status, {args['status']}, is not valid for updating an investigation. "
                f"Supported Status Values: {INVESTIGATION_STATUSES}"))

        variables["investigation"][field] = args.get(field)

    if not variables["investigation"]:
        raise ValueError(f"No valid investigation fields provided. Supported Update Fields: {INVESTIGATION_UPDATE_FIELDS}")

    result = client.graphql_run(query=query, variables=variables)

    try:
        investigation = result["data"]["updateInvestigation"]
        investigation["url"] = generate_id_url(env, "investigations", investigation["id"])
    except (KeyError, TypeError):
        raise ValueError(f"Failed to locate/update investigation: {result['errors'][0]['message']}")

    results = CommandResults(
        outputs_prefix="TaegisXDR.InvestigationUpdate",
        outputs_key_field="id",
        outputs=investigation,
        readable_output=tableToMarkdown(
            "Taegis Investigation",
            investigation,
            removeNull=True,
            url_keys=("url"),
        ),
        raw_response=result,
    )

    return results


def archive_investigation_command(client: Client, env: str, args=None):
    investigation_id = args.get("id")
    if not investigation_id:
        raise ValueError("Cannot archive investigation, missing investigation id")

    query = """
    mutation ($investigation_id: ID!) {
      archiveInvestigation(investigation_id: $investigation_id) {
        id
      }
    }
    """

    variables = {"investigation_id": investigation_id}
    result = client.graphql_run(query=query, variables=variables)
    try:
        investigation = result["data"]["archiveInvestigation"]
        status = "Successfully Archived Investigation"
    except (KeyError, TypeError):
        raise ValueError(f"Could not locate investigation with id: {investigation_id}")

    archive_results = {
        "id": investigation_id,
        "result": investigation,
        "status": status,
        "url": generate_id_url(env, "investigations", investigation_id),
    }

    results = CommandResults(
        outputs_prefix="TaegisXDR.ArchivedInvestigation",
        outputs_key_field="id",
        outputs=archive_results,
        readable_output=tableToMarkdown(
            "Taegis Investigation Archiving",
            archive_results,
            removeNull=True,
            url_keys=("url"),
        ),
        raw_response=result,
    )

    return results


def unarchive_investigation_command(client: Client, env: str, args=None):
    investigation_id = args.get("id")
    if not investigation_id:
        raise ValueError("Cannot unarchive investigation, missing investigation id")

    query = """
    mutation ($investigation_id: ID!) {
      unArchiveInvestigation(investigation_id: $investigation_id) {
        id
      }
    }
    """

    variables = {"investigation_id": investigation_id}
    result = client.graphql_run(query=query, variables=variables)
    try:
        investigation = result["data"]["unArchiveInvestigation"]
        status = "Successfully Unarchived Investigation"
    except (KeyError, TypeError):
        if result["errors"][0].get("message"):
            investigation = {}
            status = "Investigation is not currently archived"
        else:
            raise ValueError(f"Could not locate investigation with id: {investigation_id}")

    archive_results = {
        "id": investigation_id,
        "result": investigation,
        "status": status,
        "url": generate_id_url(env, "investigations", investigation_id),
    }

    results = CommandResults(
        outputs_prefix="TaegisXDR.UnarchivedInvestigation",
        outputs_key_field="id",
        outputs=archive_results,
        readable_output=tableToMarkdown(
            "Taegis Investigation Unarchiving",
            archive_results,
            removeNull=True,
            url_keys=("url"),
        ),
        raw_response=result,
    )

    return results


def test_module(client: Client) -> str:
    """
    Returns success if authentication was successful
    """
    try:
        client.test()
        return "ok"
    except DemistoException as exception:
        raise DemistoException(exception)


""" UTILITIES """


def generate_id_url(env: str, endpoint: str, element_id: str):
    element_id: str = element_id.replace('/', '%2F')
    return f"{ENV_URLS[env]['xdr']}/{endpoint}/{element_id}"


""" MAIN """


def main():
    command = demisto.command()
    demisto.info(f'Command being called is {command}')

    commands: Dict[str, Any] = {
        "fetch-incidents": fetch_incidents,
        "taegis-create-comment": create_comment_command,
        "taegis-create-investigation": create_investigation_command,
        "taegis-execute-playbook": execute_playbook_command,
        "taegis-fetch-alerts": fetch_alerts_command,
        "taegis-fetch-assets": fetch_assets_command,
        "taegis-fetch-comment": fetch_comment_command,
        "taegis-fetch-comments": fetch_comments_command,
        "taegis-fetch-endpoint": fetch_endpoint_command,
        "taegis-fetch-investigation": fetch_investigation_command,
        "taegis-fetch-investigation-alerts": fetch_investigation_alerts_command,
        "taegis-fetch-playbook-execution": fetch_playbook_execution_command,
        "taegis-fetch-users": fetch_users_command,
        "taegis-isolate-asset": isolate_asset_command,
        "taegis-update-alert-status": update_alert_status_command,
        "taegis-update-comment": update_comment_command,
        "taegis-update-investigation": update_investigation_command,
        "taegis-archive-investigation": archive_investigation_command,
        "taegis-unarchive-investigation": unarchive_investigation_command,
        "test-module": test_module,
    }

    PARAMS = demisto.params()
    try:
        if command not in commands:
            raise NotImplementedError(f'The "{command}" command has not been implemented.')

        environment = PARAMS.get("environment", "us1").lower()
        if not ENV_URLS.get(environment):
            raise ValueError(f"Unknown Environment Provided: {environment}")

        verify_cert = not PARAMS.get("insecure", False)

        client = Client(
            client_id=PARAMS.get("client_id"),
            client_secret=PARAMS.get("client_secret"),
            base_url=ENV_URLS[environment]["api"],
            proxy=PARAMS.get("proxy", False),
            verify=verify_cert,
        )
        client.auth()

        if command == "test-module":
            result = commands[command](client=client)
            return_results(result)

        elif command == "fetch-incidents":
            commands[command](client=client, max_fetch=PARAMS.get("max_fetch"), include_assets=PARAMS.get("include_assets"))
        else:
            return_results(commands[command](client=client, env=environment, args=demisto.args()))
    except Exception as e:
        error_string = str(e)
        demisto.error(f"Error running command: {e}")

        if "Unauthorized" in error_string:
            error_string = "Invalid credentials (Client ID or Client Secret)"
        return_error(f"Failed to execute {command} command. Error: {error_string}")


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