TaegisXDRv2
For integration with the Secureworks Taegis XDR platform.
Data Enrichment & Threat Intelligence · Secureworks
Details
| ID | TaegisXDRv2 |
|---|---|
| Provider | Sophos |
| Category | Data Enrichment & Threat Intelligence |
| From Version | 6.2.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix 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 |
| 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 settingsclient_id— Client ID (required)client_secret— Client Secret (required)isFetch— Fetch incidentsmax_fetch— Maximum number of incidents per fetchincidentFetchInterval— Incidents Fetch Intervalfirst_fetch— First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days)incidentType— Incident typeinclude_assets— Include Assets in Fetchfetch_type— Fetch Incident Type
Commands (21)
-
taegis-add-evidence-to-investigationAdd alerts and events to an existing investigation.
-
taegis-archive-investigationArchive an investigation.
-
taegis-create-commentCreate a comment on an investigation.
-
taegis-create-investigationCreate an Investigation within Taegis.
-
taegis-create-sharelinkCreates a ShareLink to a Taegis element, such as an alert or investigation.
-
taegis-execute-playbookExecutes a Taegis playbook instance.
-
taegis-fetch-alertsList Taegis alerts by ID.
-
taegis-fetch-assetsFetch assets based on search criteria.
-
taegis-fetch-commentFetch comment by comment ID.
-
taegis-fetch-commentsFetch comments by Investigation ID.
-
taegis-fetch-endpointFetch endpoint information.
-
taegis-fetch-eventsThis 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-investigationFetch all investigations or a specific investigation.
-
taegis-fetch-investigation-alertsFetch Alert IDs related to a specific investigation.
-
taegis-fetch-playbook-executionFetch the results of a Taegis playbook instance execution.
-
taegis-fetch-usersFetch a user by ID or email address.
-
taegis-isolate-assetIsolate a specific asset.
-
taegis-unarchive-investigationUnarchive an investigation.
-
taegis-update-alert-statusUpdate the status of an alert.
-
taegis-update-commentUpdate an existing comment.
-
taegis-update-investigationUpdate 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)