TaegisXDRv2

For integration with the Secureworks Taegis XDR platform.

Data Enrichment & Threat Intelligence · Secureworks

Details

IDTaegisXDRv2
ProviderSophos
CategoryData Enrichment & Threat Intelligence
From Version6.2.0
Docker Imagedemisto/python3:3.12.13.10116658
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
Fetch Incident Type The type of incident to fetch from Taegis (Alerts or Investigations) True
Include Assets in Fetch When using the Investigations fetch type, should assets be included? This can cause API failures or latency and should only be enabled if necessary 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-add-evidence-to-investigation

Base Command

!taegis-add-evidence-to-investigation

Inputs

Argument Name Description Required
id The investigation id to update True
alerts A list of alert IDs to add to an investigation False
events A list of event IDs to add to an investigation False
alert_query A Taegis CQL query for alerts to add to the investigation False

At least one of the inputs alerts, events, or alert_query MUST be defined

Command Example

`!taegis-add-evidence-to-investigation` id=c207ca4c-8a78-4408-a056-49f05d6eb77d alerts="alert://priv:crowdstrike:11772:1677742145475:07e2d9cc-0a04-55ec-890a-97f39d63698e"

Context Example

{
    "TaegisXDR": {
        "InvestigationEvidenceUpdate": {
            "investigationId": "c207ca4c-8a78-4408-a056-49f05d6eb77d"
        }
    }
}

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
id The investigation ID to add the comment to True

Command Example

!taegis-create-comment comment="This is a test comment" 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
title The subject or description of the investigation True
priority The priority for the investigiation [Default: 3] False
status The status for the investigation [Default: OPEN] False
alerts A list of alert IDs to add to the investigation [Default: []] False
keyFindings The Key Findings for the investigation False
type The investigation type [Default: SECURITY_INVESTIGATION] False
assigneeId The assignee for the investigation [Default: @secureworks] False
serviceDeskId A 3rd party ticket number for tracking purposes False
serviceDeskType The type of 3rd party ticket number False
tags A list of tags to add to the investigation [Default: []] False

Command Example

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

Context Example

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

taegis-create-sharelink

Base Command

!taegis-create-sharelink

Inputs

Argument Name Description Required
id The ID of the Taegis element to create a sharelink to True
type The type of Taegis element to create a sharelink with True

Command Example

!taegis-create-sharelink type=investigationId id=219da0ee-8642-4363-827c-8a6fbd479082

Context Example

{
    "TaegisXDR": {
        "ShareLink": {
            "id": "593fa115-abad-4a52-9fc4-2ec403a8a1e4",
            "url": "https://ctpx.secureworks.com/share/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,
                },
                "url": "https://ctpx.secureworks.com/alerts/c4f33b53-eaba-47ac-8272-199af0f7935b"
            }
        ]
    }
}

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
id The investigation ID to fetch comments for True
page Search page number [Default: 0] False
page_size Number of results per page [Default: 10] False
order_direction The order direction [Default: DESCENDING] False

Command Example

!taegis-fetch-comments 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 False
query If not using ID, the query to utilize when searching investigations [Default: deleted_at is null] False
page Search page number [Default: 0] False
page_size Number of results per page [Default: 10] False
order_by The field to order results by [Default: created_at] False
order_direction The order direction [Default: DESCENDING] False

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
page Search page number [Default: 0] False
page_size Number of results per page [Default: 10] False

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-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-fetch-events

Beta Command: This is a beta command, which lets you implement and test pre-release software. Since the command is beta, it might contain bugs. Updates to the command during the beta phase might include non-backward compatible features. We appreciate your feedback on the quality and usability of the command to help us identify issues, fix them, and continually improve.

Base Command

taegis-fetch-events

Input

Argument Name Description Required
ids A comma-separated list of event IDs to return. False
cql_query The Taegis CQL query string to use for searching events (e.g. FROM process EARLIEST=-1d \| head 10). If not defined, defaults to FROM * EARLIEST=-1m \| head 50. False
limit The maximum number of events to return. For standard CQL searches, the limit is passed as a GraphQL variable. For user-provided queries, embed the limit directly in the CQL string (e.g., \| head 100). Default is 50. False
offset The number of events to skip before returning results. Default is 0. False
next The pagination cursor token returned from a previous taegis-fetch-events call. Use this to retrieve the next page of results. False
fields The fields to return from the query. False
tenant_id The tenant to run against if using an MSP. If no tenant is provided, the tenant of the generated credentials is used. False

CQL Query Time Field Reference

Scenario Use This Field Why?
Incident Reconstruction event_time You need to see the exact sequence of the attacker’s steps.
Real-time Monitoring EARLIEST=-1m You want to see everything that hits the platform in the last 60 seconds.
Compliance/Audit ingest_time You need to prove when Secureworks actually received the record.
Offline Host Sync ingest_time You want to find data from a laptop that was just turned back on after a weekend.

Command example

!taegis-fetch-events
!taegis-fetch-events cql_query="FROM process EARLIEST=-1d | head 10"
!taegis-fetch-events cql_query="FROM dnsquery WHERE query_name MATCHES ('*.xyz', '*.top') EARLIEST=-24h" limit=100
!taegis-fetch-events ids="event-12345-67890,event-12345-67891"
!taegis-fetch-events next="eyJvZmZzZXQiOiAxMH0="

Context Example

[
    {
        "TaegisXDR": {
            "Events": [
                {
                    "id": "event-12345-67890",
                    "metadata": {
                        "event_type": "process",
                        "event_time": "2024-05-20T14:30:05.123Z",
                        "tenant_id": "999-000-111",
                        "sensor_id": "win-endpoint-01"
                    },
                    "parent_process_id": "456",
                    "image_path": "C:\\Windows\\System32\\cmd.exe",
                    "commandline": "cmd.exe /c \"whoami\"",
                    "username": "admin_user",
                    "next": "CursorToken_Batch01_Seq99"
                },
                {
                    "id": "event-12345-67891",
                    "metadata": {
                        "event_type": "netflow",
                        "event_time": "2024-05-20T14:30:10.456Z",
                        "tenant_id": "999-000-111",
                        "sensor_id": "fw-edge-02"
                    },
                    "source_ip": "1.1.1.1",
                    "destination_ip": "8.8.8.8",
                    "destination_port": 53,
                    "protocol": "UDP",
                    "next": "CursorToken_Batch01_Seq99"
                }
            ]
        }
    }
]

Context Output

Path Type Description
TaegisXDR.Events.id String The unique identifier of the event.
TaegisXDR.Events.metadata.event_type String The type of event (e.g., process, netflow, dnsquery).
TaegisXDR.Events.metadata.event_time String The timestamp when the event occurred.
TaegisXDR.Events.metadata.tenant_id String The tenant ID associated with the event.
TaegisXDR.Events.metadata.sensor_id String The sensor ID that generated the event.
TaegisXDR.Events.parent_process_id String The parent process ID (process events).
TaegisXDR.Events.image_path String The image/executable path (process events).
TaegisXDR.Events.commandline String The command line string (process events).
TaegisXDR.Events.username String The username associated with the event.
TaegisXDR.Events.source_ip String The source IP address (netflow events).
TaegisXDR.Events.destination_ip String The destination IP address (netflow events).
TaegisXDR.Events.destination_port Number The destination port number (netflow events).
TaegisXDR.Events.protocol String The network protocol (netflow events).
TaegisXDR.Events.next String Pagination cursor token for retrieving the next page of results.

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
  • OTHER

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
title The title of the investigation False
keyFindings The investigation Key Findings False
prioirity The priority of the Investigation (1-5) False
status The current status of the Investigation False
assigneeId The id of a user to assign, in auth0|12345 format False
serviceDeskId A 3rd party ticket number for tracking purposes False
serviceDeskType The type of 3rd party ticket number False
tags A list of tags to add to the investigation [Default: []] 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"

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
  • incidentFetchInterval — Incidents Fetch Interval
  • first_fetch — First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days)
  • incidentType — Incident type
  • include_assets — Include Assets in Fetch
  • fetch_type — Fetch Incident Type

Commands (21)

  • taegis-add-evidence-to-investigation

    Add alerts and events to an existing investigation.

  • taegis-archive-investigation

    Archive an investigation.

  • taegis-create-comment

    Create a comment on an investigation.

  • taegis-create-investigation

    Create an Investigation within Taegis.

  • taegis-create-sharelink

    Creates a ShareLink to a Taegis element, such as an alert or investigation.

  • 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 Investigation ID.

  • taegis-fetch-endpoint

    Fetch endpoint information.

  • taegis-fetch-events

    This is a beta command, which lets you implement and test pre-release software. Since the command is beta, it might contain bugs. Updates to the command during the beta phase might include non-backward compatible features. We appreciate your feedback on the quality and usability of the command to help us identify issues, fix them, and continually improve. Fetches Taegis events using a CQL query string or a list of event IDs. Supports pagination via the next cursor token returned in results. If no cql_query is provided, defaults to "FROM * EARLIEST=-1m | head 50".

  • 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 pytest

from CommonServerPython import DemistoException

from TaegisXDRv2 import (
    Client,
    execute_playbook_command,
    fetch_alerts_command,
    fetch_assets_command,
    create_comment_command,
    fetch_comment_command,
    fetch_comments_command,
    update_comment_command,
    fetch_endpoint_command,
    fetch_incidents,
    fetch_investigation_command,
    fetch_investigation_alerts_command,
    fetch_users_command,
    fetch_playbook_execution_command,
    isolate_asset_command,
    create_investigation_command,
    update_investigation_command,
    archive_investigation_command,
    unarchive_investigation_command,
    update_alert_status_command,
    add_evidence_to_investigation_command,
    create_sharelink_command,
    fetch_events_command,
    test_module as connectivity_test,
)

from test_data.data import *  # nopycln: import


""" UTILITY FUNCTIONS """


def mock_client(requests_mock, mock_response):
    base_url = "https://api.ctpx.secureworks.com"

    requests_mock.post(f"{base_url}/graphql", json=mock_response)
    requests_mock.get(f"{base_url}/assets/version", json=mock_response)
    client = Client(
        client_id="TestID",
        client_secret="TestSecret",
        base_url=base_url,
    )
    return client


""" TESTS """


def test_execute_playbook(requests_mock):
    """Tests taegis-execute-playbook command function"""
    client = mock_client(requests_mock, EXECUTE_PLAYBOOK_RESPONSE)
    args = {
        "id": TAEGIS_PLAYBOOK_INSTANCE_ID,
        "inputs": {
            "MyInput": "MyValue",
        },
    }

    response = execute_playbook_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs["id"] == TAEGIS_PLAYBOOK_EXECUTION_ID

    with pytest.raises(ValueError, match="Cannot execute playbook, missing playbook_id"):
        assert execute_playbook_command(client=client, env=TAEGIS_ENVIRONMENT, args={})

    client = mock_client(requests_mock, EXECUTE_PLAYBOOK_BAD_RESPONSE)
    with pytest.raises(ValueError, match="Failed to execute playbook: must be defined"):
        assert execute_playbook_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)


def test_fetch_alerts(requests_mock):
    client = mock_client(requests_mock, FETCH_ALERTS_RESPONSE)
    args = {
        "limit": 1,
        "offset": 0,
        "cql_query": "from alert severity >= 0.6 and status='OPEN'",
    }

    # Test with no IDs set
    response = fetch_alerts_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs[0] == TAEGIS_ALERT
    assert len(response.outputs) == len([TAEGIS_ALERT])


def test_fetch_alerts_by_id(requests_mock):
    """Tests taegis-fetch-alert command function"""
    client = mock_client(requests_mock, FETCH_ALERTS_BY_ID_RESPONSE)

    # Test with IDs set (list)
    args = {"ids": ["alert://priv:crowdstrike:11772:1666247222095:4e41ec02-ca53-5ff7-95cc-eda434221ba6"]}
    response = fetch_alerts_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs[0] == TAEGIS_ALERT
    assert len(response.outputs) == len([TAEGIS_ALERT])

    # Test with IDs set (comma separated list)
    args = {"ids": "alert://priv:crowdstrike:11772:1666247222095:4e41ec02-ca53-5ff7-95cc-eda434221ba6"}
    response = fetch_alerts_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs[0] == TAEGIS_ALERT
    assert len(response.outputs) == len([TAEGIS_ALERT])


def test_fetch_assets(requests_mock):
    """Tests taegis-fetch-assets command function"""

    client = mock_client(requests_mock, FETCH_ASSETS_RESPONSE)
    args = {
        "page": 0,
        "page_size": 1,
    }

    response = fetch_assets_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs == [TAEGIS_ASSET]

    # Test allowed search fields
    args = {"host_id": TAEGIS_ASSET["hostId"]}
    response = fetch_assets_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs[0] == TAEGIS_ASSET
    assert len(response.outputs) == len([TAEGIS_ASSET])

    # Asset Query Failure
    client = mock_client(requests_mock, FETCH_ASSETS_BAD_RESPONSE)
    with pytest.raises(ValueError, match="Failed to fetch assets:"):
        assert fetch_assets_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)


def test_create_comment(requests_mock):
    """Tests taegis-create-comment command function"""
    client = mock_client(requests_mock, CREATE_COMMENT_RESPONSE)

    # comment not set
    with pytest.raises(ValueError, match="Cannot create comment, comment cannot be empty"):
        assert create_comment_command(client=client, env=TAEGIS_ENVIRONMENT, args={})

    # id not set
    with pytest.raises(ValueError, match="Cannot create comment, id cannot be empty"):
        assert create_comment_command(client=client, env=TAEGIS_ENVIRONMENT, args={"comment": "Test comment"})

    args = {
        "comment": "Test Comment",
        "id": "12345-12345-12345",
        "fields": "id",
    }

    # Successful fetch - Comment created
    response = create_comment_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs == CREATE_COMMENT_RESPONSE["data"]["addCommentToInvestigation"]

    # Comment creation failed
    client = mock_client(requests_mock, CREATE_UPDATE_COMMENT_BAD_RESPONSE)
    with pytest.raises(ValueError, match="Failed to create comment:"):
        assert create_comment_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)


def test_fetch_comment_by_id(requests_mock):
    """Tests taegis-fetch-comment command function"""
    client = mock_client(requests_mock, FETCH_COMMENT_RESPONSE)

    # comment_id not set
    with pytest.raises(ValueError, match="Cannot fetch comment, missing comment_id"):
        assert fetch_comment_command(client=client, env=TAEGIS_ENVIRONMENT, args={})

    args = {"id": "ff9ca818-4749-4ccb-883a-2ccc6f6c9e0f"}

    # Successful fetch - Comment found
    response = fetch_comment_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs == FETCH_COMMENT_RESPONSE["data"]["comment"]

    # Comment not found
    client = mock_client(requests_mock, {})
    with pytest.raises(ValueError, match="Could not locate comment by provided ID"):
        assert fetch_comment_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)


def test_fetch_comments(requests_mock):
    """Tests taegis-fetch-comments command function"""
    client = mock_client(requests_mock, FETCH_COMMENTS_RESPONSE)

    # comment_id not set
    with pytest.raises(ValueError, match="Cannot fetch comments, missing id"):
        assert fetch_comments_command(client=client, env=TAEGIS_ENVIRONMENT, args={})

    args = {
        "id": "c2e09554-833e-41a1-bc9d-8160aec0d70d",
    }

    # Successful fetch
    response = fetch_comments_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs == FETCH_COMMENTS_RESPONSE["data"]["commentsV2"]["comments"]

    # Comment not found, bad response
    client = mock_client(requests_mock, FETCH_COMMENTS_BAD_RESPONSE)
    response = fetch_comments_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs == []


def test_update_comment(requests_mock):
    """Tests taegis-update-comment command function"""
    client = mock_client(requests_mock, UPDATE_COMMENT_RESPONSE)

    # comment not set
    with pytest.raises(ValueError, match="Cannot update comment, comment id cannot be empty"):
        assert update_comment_command(client=client, env=TAEGIS_ENVIRONMENT, args={})

    # comment_id not set
    with pytest.raises(ValueError, match="Cannot update comment, comment cannot be empty"):
        assert update_comment_command(client=client, env=TAEGIS_ENVIRONMENT, args={"id": "test"})

    args = {
        "comment": FETCH_COMMENT_RESPONSE["data"]["comment"]["comment"],
        "id": FETCH_COMMENT_RESPONSE["data"]["comment"]["id"],
    }

    #  # Successful fetch - Comment created
    response = update_comment_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs == UPDATE_COMMENT_RESPONSE["data"]["updateInvestigationComment"]

    # Comment creation failed
    client = mock_client(requests_mock, CREATE_UPDATE_COMMENT_BAD_RESPONSE)
    with pytest.raises(ValueError, match="Failed to locate/update comment:"):
        assert update_comment_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)


def test_fetch_endpoint(requests_mock):
    """Tests taegis-fetch-endpoint command function"""
    client = mock_client(requests_mock, FETCH_ENDPOINT_RESPONSE)

    # comment_id not set
    with pytest.raises(ValueError, match="Cannot fetch endpoint information, missing id"):
        assert fetch_endpoint_command(client=client, env=TAEGIS_ENVIRONMENT, args={})

    args = {"id": "110d1fd3a23c95c0120d0d10451cb001"}

    # Successful fetch - Endpoint found
    response = fetch_endpoint_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs == FETCH_ENDPOINT_RESPONSE["data"]["assetEndpointInfo"]

    # Endpoint not found
    client = mock_client(requests_mock, FETCH_ENDPOINT_BAD_RESPONSE)
    with pytest.raises(ValueError, match="Failed to fetch endpoint information"):
        assert fetch_endpoint_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)


def test_connectivity(requests_mock):
    response = {"revision": "1e9c12c7f3f51d5ecc0da91d1ac8bcb27e3566a7", "timestamp": "2022-01-01T17:02:01Z"}
    client = mock_client(requests_mock, response)
    assert connectivity_test(client=client) == "ok"


def test_fetch_incidents_alerts(requests_mock):
    """Tests taegis-fetch-incidents (alerts) command function"""
    client = mock_client(requests_mock, FETCH_ALERTS_RESPONSE)
    response = fetch_incidents(client=client, env=TAEGIS_ENVIRONMENT, fetch_type="alerts")
    assert response[0]["name"] == FETCH_ALERTS_RESPONSE["data"]["alertsServiceSearch"]["alerts"]["list"][0]["metadata"]["title"]


def test_fetch_incidents_investigations(requests_mock):
    """Tests taegis-fetch-incidents (investigations) command function"""
    client = mock_client(requests_mock, FETCH_INCIDENTS_RESPONSE)
    response = fetch_incidents(client=client, env=TAEGIS_ENVIRONMENT)
    assert response[0]["name"] == FETCH_INCIDENTS_RESPONSE["data"]["investigationsSearch"]["investigations"][0]["description"]

    # Invalid max_fetch
    with pytest.raises(ValueError, match="Max Fetch must be between 1 and 200"):
        assert fetch_incidents(client=client, env=TAEGIS_ENVIRONMENT, max_fetch=0)
    with pytest.raises(ValueError, match="Max Fetch must be between 1 and 200"):
        assert fetch_incidents(client=client, env=TAEGIS_ENVIRONMENT, max_fetch=201)

    # Invalid fetch_type
    with pytest.raises(ValueError, match="Incident Type is invalid"):
        assert fetch_incidents(client=client, env=TAEGIS_ENVIRONMENT, fetch_type="BAD_TYPE")

    # Failure from Taegis API
    client = mock_client(requests_mock, FETCH_INCIDENTS_BAD_RESPONSE)
    error = f"Error when fetching incidents: {FETCH_INCIDENTS_BAD_RESPONSE['errors'][0]['message']}"
    with pytest.raises(DemistoException, match=error):
        assert fetch_incidents(client=client, env=TAEGIS_ENVIRONMENT, max_fetch=200)

    # Unknown error
    client = mock_client(requests_mock, {})
    response = fetch_incidents(client=client, env=TAEGIS_ENVIRONMENT)
    assert len(response) == 0

    # Ignore incidents that have been archived
    FETCH_INCIDENTS_RESPONSE["data"]["investigationsSearch"]["investigations"][0]["archived_at"] = "2022-02-03T13:53:35Z"
    client = mock_client(requests_mock, FETCH_INCIDENTS_RESPONSE)
    response = fetch_incidents(client=client, env=TAEGIS_ENVIRONMENT)
    assert len(response) == 0


def test_fetch_investigation(requests_mock):
    """Tests taegis-fetch-investigation command function

    Test fetching of a single incident
    """
    client = mock_client(requests_mock, FETCH_INVESTIGATION_RESPONSE)
    args = {
        "id": "c2e09554-833e-41a1-bc9d-8160aec0d70d",
        "page": 0,
        "page_sie": 1,
    }

    response = fetch_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs[0] == TAEGIS_INVESTIGATION
    assert response.outputs[0]["url"] == f"{TAEGIS_URL}/investigations/{args['id']}"

    # Investigation not found
    mock_result = {"errors": [{"message": "record not found"}], "data": {"investigationV2": None}}
    client = mock_client(requests_mock, mock_result)
    response = fetch_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert len(response.outputs) == 0

    # Invalid query response
    client = mock_client(requests_mock, {})
    response = fetch_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert len(response.outputs) == 0


def test_fetch_investigations(requests_mock):
    """Tests taegis-fetch-investigations command function

    Test fetching of all incidents
    """

    client = mock_client(requests_mock, FETCH_INVESTIGATIONS_RESPONSE)
    args = {
        "page": 0,
        "page_size": 1,
    }

    response = fetch_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs == [TAEGIS_INVESTIGATION]


def test_fetch_investigation_alerts(requests_mock):
    """Tests taegis-fetch-investigation-alerts command function"""
    client = mock_client(requests_mock, FETCH_INVESTIGATION_ALERTS_RESPONSE)
    args = {
        "id": "c2e09554-833e-41a1-bc9d-8160aec0d70d",
    }

    response = fetch_investigation_alerts_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs[0] == TAEGIS_ALERT
    assert len(response.outputs) == len([TAEGIS_ALERT])

    # No alerts returned
    client = mock_client(requests_mock, {})
    response = fetch_investigation_alerts_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert len(response.outputs) == 0

    # Investigation ID not provided
    with pytest.raises(ValueError, match="Cannot fetch investigation, missing investigation_id"):
        assert fetch_investigation_alerts_command(client=client, env=TAEGIS_ENVIRONMENT, args={})


def test_fetch_playbook_execution(requests_mock):
    """Tests taegis-fetch-playbook-execution command function"""
    client = mock_client(requests_mock, FETCH_PLAYBOOK_EXECUTION_RESPONSE)
    args = {
        "id": TAEGIS_PLAYBOOK_EXECUTION_ID,
    }

    response = fetch_playbook_execution_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs == TAEGIS_PLAYBOOK_EXECUTION

    client = mock_client(requests_mock, FETCH_PLAYBOOK_EXECUTION_RESPONSE)
    with pytest.raises(ValueError, match="Cannot fetch playbook execution, missing execution id"):
        assert fetch_playbook_execution_command(client=client, env=TAEGIS_ENVIRONMENT, args={})

    client = mock_client(requests_mock, FETCH_PLAYBOOK_EXECUTION_BAD_RESPONSE)
    with pytest.raises(ValueError, match="Failed to fetch playbook execution"):
        assert fetch_playbook_execution_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)


def test_create_investigation(requests_mock):
    """Tests taegis-create-investigation command function"""
    client = mock_client(requests_mock, CREATE_INVESTIGATION_RESPONSE)

    # Invalid assignee_id
    with pytest.raises(ValueError, match="assigneeId MUST be in 'auth0|12345' format or '@secureworks'"):
        assert create_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args={"assignee_id": "BadID"})

    # Invalid priority
    with pytest.raises(ValueError, match="Priority must be between 1-4"):
        assert create_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args={"priority": 7})

    # Invalid status
    with pytest.raises(ValueError, match="The provided status, BAD_STATUS, is not valid for updating an investigation"):
        assert create_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args={"status": "BAD_STATUS"})

    # Invalid type
    with pytest.raises(ValueError, match="The provided type, BAD_TYPE, is not valid for updating an investigation."):
        assert create_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args={"type": "BAD_TYPE"})

    # Invalid title
    with pytest.raises(ValueError, match="Title must be defined"):
        assert create_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args={})

    # Successul Creation
    response = create_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args={"title": "Test Title"})
    assert response.outputs["id"] == CREATE_INVESTIGATION_RESPONSE["data"]["createInvestigationV2"]["id"]

    # Investigation creation failed
    client = mock_client(requests_mock, FETCH_INCIDENTS_BAD_RESPONSE)
    with pytest.raises(ValueError, match="Failed to create investigation:"):
        assert create_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args={"title": "Test Title"})


def test_update_investigation(requests_mock):
    """Tests taegis-update-investigation command function"""
    client = mock_client(requests_mock, UPDATE_INVESTIGATION_RESPONSE)
    args = {}

    # id not set
    with pytest.raises(ValueError, match="Cannot fetch investigation without id defined"):
        assert update_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)

    args["id"] = UPDATE_INVESTIGATION_RESPONSE["data"]["updateInvestigationV2"]["id"]

    # Invalid investigation status
    args["status"] = "BadStatus"
    bad_status = r"The provided status, BadStatus, is not valid for updating an investigation. Supported Status Values:.*"
    with pytest.raises(ValueError, match=bad_status):
        assert update_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)

    args["status"] = "ACTIVE"

    # Invalid assigneeId Format
    args["assigneeId"] = "BadAssigneeIDFormat"
    with pytest.raises(ValueError, match="assigneeId MUST be in 'auth0|12345' format or '@secureworks'"):
        assert update_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)

    args["assigneeId"] = "@secureworks"

    # Invalid priority
    args["priority"] = 10
    with pytest.raises(ValueError, match="Priority must be between 1-4"):
        assert update_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)

    args["priority"] = 1

    # Invalid type
    args["type"] = "BAD_TYPE"
    with pytest.raises(ValueError, match="The provided type, BAD_TYPE, is not valid for updating an investigation."):
        assert update_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)

    # No valid update fields set
    args = {"id": UPDATE_INVESTIGATION_RESPONSE["data"]["updateInvestigationV2"]["id"]}
    with pytest.raises(ValueError, match="No valid investigation fields provided. Supported Update Fields"):
        assert update_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)

    # Tags defined
    args["tags"] = "test_tag01"
    response = update_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs["id"] == args["id"]

    # Successful Update
    args = {
        "id": UPDATE_INVESTIGATION_RESPONSE["data"]["updateInvestigationV2"]["id"],
        "title": "Test Investigation Updated",
        "priority": 2,
        "status": "ACTIVE",
    }
    response = update_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs["id"] == args["id"]

    # Investigation update failure
    client = mock_client(requests_mock, FETCH_COMMENTS_BAD_RESPONSE)
    with pytest.raises(ValueError, match="Failed to locate/update investigation"):
        assert update_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)


def test_archive_investigation(requests_mock):
    """Tests taegis-archive-investigation command function"""
    client = mock_client(requests_mock, INVESTIGATION_ARCHIVE_RESPONSE)

    # Test Archiving
    args = {"id": TAEGIS_INVESTIGATION["id"]}
    response = archive_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs["id"] == args["id"]
    assert response.raw_response["data"]["archiveInvestigation"]

    # investigation id not set
    with pytest.raises(ValueError, match="Cannot archive investigation, missing investigation id"):
        assert archive_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args={})

    # Investigation archive not found
    client = mock_client(requests_mock, INVESTIGATION_ARCHIVE_ALREADY_COMPLETE)
    with pytest.raises(ValueError, match="Could not locate investigation with id:.*"):
        assert archive_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)


def test_unarchive_investigation(requests_mock):
    """Tests taegis-unarchive-investigation command function"""
    client = mock_client(requests_mock, INVESTIGATION_UNARCHIVE_RESPONSE)

    # Test Unarchiving
    args = {"id": TAEGIS_INVESTIGATION["id"]}
    response = unarchive_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs["id"] == args["id"]
    assert response.raw_response["data"]["unArchiveInvestigation"]

    # investigation id not set
    with pytest.raises(ValueError, match="Cannot unarchive investigation, missing investigation id"):
        assert unarchive_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args={})

    # Investigation is not archived
    client = mock_client(requests_mock, INVESTIGATION_ARCHIVE_ALREADY_COMPLETE)
    response = unarchive_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs["id"] == args["id"]
    assert response.outputs["status"] == "Investigation is not currently archived"

    # Could not find investigation by investigation id
    args = {"id": "InvalidInvestigationId"}
    client = mock_client(requests_mock, INVESTIGATION_NOT_ARCHIVED_RESPONSE)
    with pytest.raises(ValueError, match="Could not locate investigation with id:.*"):
        assert unarchive_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)


def test_isolate_asset(requests_mock):
    """Tests taegis-isolate-asset command function"""
    client = mock_client(requests_mock, ISOLATE_ASSET_RESPONSE)

    # asset id not set
    with pytest.raises(ValueError, match="Cannot isolate asset, missing id"):
        assert isolate_asset_command(client=client, env=TAEGIS_ENVIRONMENT, args={})
    args = {"id": TAEGIS_ASSET["id"]}

    # reason not set
    with pytest.raises(ValueError, match="Cannot isolate asset, missing reason"):
        assert isolate_asset_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    args["reason"] = "My isolation reason"

    # Successful isolation
    response = isolate_asset_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs == ISOLATE_ASSET_RESPONSE["data"]["isolateAsset"]

    # Endpoint not found
    client = mock_client(requests_mock, ISOLATE_ASSET_BAD_RESPONSE)
    with pytest.raises(ValueError, match="Failed to isolate asset"):
        assert isolate_asset_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)


def test_fetch_users(requests_mock):
    client = mock_client(requests_mock, FETCH_USERS_RESPONSE)
    args = {
        "limit": 1,
        "page_size": 0,
    }

    response = fetch_users_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs[0] == TAEGIS_USER
    assert len(response.outputs) == len([TAEGIS_USER])

    client = mock_client(requests_mock, FETCH_USERS_BAD_RESPONSE)
    with pytest.raises(ValueError, match="Failed to fetch user information:"):
        assert fetch_users_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)

    # Test user search by email
    client = mock_client(requests_mock, FETCH_USERS_RESPONSE)
    args["email"] = TAEGIS_USER["email"]
    response = fetch_users_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs[0] == TAEGIS_USER
    assert len(response.outputs) == len([TAEGIS_USER])

    # Test user search by auth0 user id
    client = mock_client(requests_mock, FETCH_USER_RESPONSE)
    args["id"] = TAEGIS_USER["user_id"]
    args.pop("email")
    response = fetch_users_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs[0] == TAEGIS_USER
    assert len(response.outputs) == len([TAEGIS_USER])

    # Invalid id Format
    args["id"] = "BadAssigneeIDFormat"
    invalid_fields = r"id MUST be in 'auth0|12345' format"
    with pytest.raises(ValueError, match=invalid_fields):
        assert fetch_users_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)


def test_update_alert_status(requests_mock):
    """Tests taegis-update-alert-status command function"""
    client = mock_client(requests_mock, UPDATE_ALERT_STATUS_RESPONSE)

    args = {"ids": TAEGIS_ALERT["id"]}

    # alert ids not set
    with pytest.raises(ValueError, match="Alert IDs must be defined"):
        assert update_alert_status_command(client=client, env=TAEGIS_ENVIRONMENT, args={})

    # status not set
    with pytest.raises(ValueError, match="Alert status must be defined"):
        assert update_alert_status_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)

    args["status"] = "Bad Status"
    with pytest.raises(ValueError, match="The provided status, Bad Status, is not valid for updating an alert"):
        assert update_alert_status_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)

    # Successful update
    args["status"] = "NOT_ACTIONABLE"
    response = update_alert_status_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs == UPDATE_ALERT_STATUS_RESPONSE["data"]["alertsServiceUpdateResolutionInfo"]

    # Alert not updated
    client = mock_client(requests_mock, UPDATE_ALERT_STATUS_BAD_RESPONSE)
    with pytest.raises(ValueError, match="Failed to locate/update alert"):
        assert update_alert_status_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)


def test_add_evidence_to_investigation(requests_mock):
    """Tests taegis-add-evidence-to-investigation command function"""
    alerts = ["alert://priv:crowdstrike:11772:1666247222095:4e41ec02-ca53-5ff7-95cc-eda434221ba6"]

    client = mock_client(requests_mock, TAEGIS_ADD_EVIDENCE_TO_INVESTIGATION_RESPONSE)
    args = {
        "id": UPDATE_INVESTIGATION_RESPONSE["data"]["updateInvestigationV2"]["id"],
        "alerts": alerts,
    }
    response = add_evidence_to_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)

    assert response.outputs["investigationId"] == args["id"]

    args = {}

    # investigation_id not set
    with pytest.raises(ValueError, match="Cannot add evidence to investigation, id cannot be empty"):
        assert add_evidence_to_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)

    args["id"] = UPDATE_INVESTIGATION_RESPONSE["data"]["updateInvestigationV2"]["id"]

    # alerts not set
    with pytest.raises(ValueError, match="Cannot add evidence to investigation. alerts, events, or alert_query must be defined"):
        assert add_evidence_to_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)

    args["alerts"] = alerts

    # Unknown error
    client = mock_client(requests_mock, {"errors": [{"message": "Unknown Error"}]})
    with pytest.raises(ValueError, match="Failed to create investigation"):
        assert add_evidence_to_investigation_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)


def test_create_sharelink(requests_mock):
    """Tests taegis-create-sharelink function"""

    client = mock_client(requests_mock, CREATE_SHARELINK_RESPONSE)

    args = {"tenant_id": "123456"}

    # id not set
    with pytest.raises(ValueError, match="Cannot create ShareLink, id cannot be empty"):
        assert create_sharelink_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)

    # type not set
    args["id"] = UPDATE_INVESTIGATION_RESPONSE["data"]["updateInvestigationV2"]["id"]
    with pytest.raises(ValueError, match="Cannot create ShareLink, type cannot be empty"):
        assert create_sharelink_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)

    # Unknown type
    args["type"] = "BAD_TYPE"
    with pytest.raises(ValueError, match="The provided ShareLink type, BAD_TYPE, is not valid for creating a ShareLink."):
        assert create_sharelink_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)

    # Successful Creation
    args["type"] = "investigationId"
    response = create_sharelink_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs["id"] == CREATE_SHARELINK_RESPONSE["data"]["createShareLink"]["id"]

    # Unknown error
    client = mock_client(requests_mock, {"errors": [{"message": "Unknown Error"}]})
    with pytest.raises(ValueError, match="Failed to create ShareLink"):
        assert create_sharelink_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)


def test_fetch_events(requests_mock):
    """Tests taegis-fetch-events command function"""

    client = mock_client(requests_mock, FETCH_EVENTS_RESPONSE)

    # Successful query with default cql_query (no args)
    response = fetch_events_command(client=client, env=TAEGIS_ENVIRONMENT, args={})
    assert response.outputs == FETCH_EVENTS_RESPONSE["data"]["eventsServiceSearch"]
    assert len(response.outputs) == 1
    assert response.outputs[0] == TAEGIS_EVENT

    # Successful query with explicit cql_query
    args = {
        "cql_query": "FROM process EARLIEST=-1d | head 10",
        "limit": 10,
        "offset": 0,
    }
    response = fetch_events_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs == FETCH_EVENTS_RESPONSE["data"]["eventsServiceSearch"]
    # next cursor is embedded in the last event object
    assert response.outputs[0].get("next") is None

    # Query with next page token returned
    client = mock_client(requests_mock, FETCH_EVENTS_NEXT_PAGE_RESPONSE)
    response = fetch_events_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)
    assert response.outputs[0].get("next") == TAEGIS_EVENT_NEXT_PAGE
    assert len(response.outputs) == 1

    # Fetch by IDs
    client = mock_client(requests_mock, FETCH_EVENTS_BY_ID_RESPONSE)
    args_by_id = {"ids": [TAEGIS_EVENT["id"]]}
    response = fetch_events_command(client=client, env=TAEGIS_ENVIRONMENT, args=args_by_id)
    assert response.outputs == FETCH_EVENTS_BY_ID_RESPONSE["data"]["eventsServiceRetrieveEventsById"]

    # Fetch next page using cursor
    client = mock_client(requests_mock, FETCH_EVENTS_PAGE_RESPONSE)
    args_next = {"next": TAEGIS_EVENT_NEXT_PAGE}
    response = fetch_events_command(client=client, env=TAEGIS_ENVIRONMENT, args=args_next)
    assert response.outputs == FETCH_EVENTS_PAGE_RESPONSE["data"]["eventsServiceEventPage"]

    # Query failure
    client = mock_client(requests_mock, FETCH_EVENTS_BAD_RESPONSE)
    with pytest.raises(ValueError, match="Failed to fetch events: invalid CQL query"):
        assert fetch_events_command(client=client, env=TAEGIS_ENVIRONMENT, args=args)