XDR Python SDK Examples๐
Detections๐
Search Detections๐
The following example performs a search of detections using the query language and returns a number of common fields of interest. This query example returns High/Critical detections that were created within the earliest/latest timeframe that have not received feedback yet.
Note
All timestamps used in this example are UTC.
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.alerts.types import SearchRequestInput
service = GraphQLService()
results = service.alerts.query.alerts_service_search(SearchRequestInput(
cql_query="FROM alert WHERE severity >= 0.6 AND status = 'OPEN' EARLIEST=-1d",
limit=10,
offset=0,
))
print(results)
Retrieve Detections by ID๐
The following query can be used to retrieve a detection by ID.
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.alerts.types import GetByIDRequestInput
service = GraphQLService()
results = service.alerts.query.alerts_service_retrieve_alerts_by_id(GetByIDRequestInput(i_ds=[
"alert://priv:stolen-user-credentials:11063:1630602244467:79015c9a-8d22-5c4e-a199-58afc0599aa5"
]))
print(results)
Resolve Detections by ID๐
Use the following to resolve a list of Detections by their IDs. This accepts a list of one or more detection IDs, the reason for resolving, and the resolution_status to label the detections with.
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.alerts.types import UpdateResolutionRequestInput, ResolutionStatus, CallerInformation
service = GraphQLService()
results = service.alerts.mutation.alerts_service_update_resolution_info(UpdateResolutionRequestInput(
alert_ids=[
"alert://priv:event-filter:11063:1645647767642:d9ea3f17-5b8b-4edc-93e1-29539b7c6b26",
"alert://priv:event-filter:11063:1645647041875:37ee1876-8458-4897-8965-f0c7b9afc85e"
],
reason="This is why I am resolving.",
resolution_status=ResolutionStatus.FALSE_POSITIVE,
caller=CallerInformation.ALERTS_V2,
))
print(results)
Aggregate Detection Data๐
Use the following query to retrieve detection aggregate counts by severity.
Note
This is similar to the deprecated alertsBySeverity query.
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.alerts.types import SearchRequestInput
service = GraphQLService()
with service(output="status reason alerts { group_by { key value } total_results }"):
results = service.alerts.query.alerts_service_search(SearchRequestInput(
cql_query="FROM alert WHERE severity >= 0.6 AND status = 'OPEN' EARLIEST=-1d | aggregate count by severity | head 10",
limit=1,
offset=0,
))
print(results)
Detections Pagination๐
There are two primary methods of pagination. The first allows for retrieval of up to 10,000 detections. The other allows for retrieval of up to 1,000,000 detections.
Under 10k Detections๐
Paginate search requests using the limit and offset fields.
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.alerts.types import SearchRequestInput
service = GraphQLService()
results_page_1 = service.alerts.query.alerts_service_search(SearchRequestInput(
cql_query="FROM alert EARLIEST=-24h",
limit=500,
offset=0,
))
print(results_page_1)
results_page_2 = service.alerts.query.alerts_service_search(SearchRequestInput(
cql_query="FROM alert EARLIEST=-24h",
limit=500,
offset=500,
))
print(results_page_2)
Over 10k Detections๐
The other method of pagination is to use the search_id returned by a search request. This allows for retrieval of more than 10,000 results.
First, submit a search request with the limit set to the total number of results desired. In the following example, we are requesting 50,000 total results.
If there are more than 10,000 results for your search, you will get back a search_id you can use to retrieve subsequent pages along with the first 10,000 results. To retrieve subsequent pages, send the same search request, but add the search_id parameter and remove the limit and offset parameters. You have reached the end of the result set when you no longer get a search_id back.
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.alerts.types import SearchRequestInput, AlertsResponse
service = GraphQLService()
query = "FROM alert WHERE severity >= 0.6 AND status = 'OPEN' EARLIEST=-3d"
results = service.alerts.query.alerts_service_search(SearchRequestInput(
cql_query=query,
limit=50000,
))
responses = [results]
search_id = results.search_id
total_parts = results.alerts.total_parts
while search_id:
results = None
try:
results = service.alerts.query.alerts_service_search(
SearchRequestInput(
search_id=search_id,
cql_query=query,
)
)
search_id = None
except Exception as exc:
if "not found" in str(exc):
break
raise exc
if (
isinstance(results, AlertsResponse)
and results.alerts is not None
):
responses.append(results)
print(sum(
len(response.alerts.list)
for response in poll_responses
))
More๐
See the Alerts GraphQL API Documentation for a list of available Detections GraphQL objects.
Assets๐
Show All Assets๐
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.assets.types import AssetsOrderByInput, AssetStateFilter
service = GraphQLService()
results = service.assets.query.all_assets(
offset=0,
limit=3,
order_by=AssetsOrderByInput.HOSTNAME,
filter_asset_state=AssetStateFilter.ALL
)
print(results)
Pagination๐
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.assets.types import AssetsOrderByInput, AssetStateFilter
service = GraphQLService()
offset = 0
limit = 500
results = service.assets.query.all_assets(
offset=offset,
limit=limit,
order_by=AssetsOrderByInput.HOSTNAME,
filter_asset_state=AssetStateFilter.ALL
)
assets = results.assets
while len(results.assets) != limit:
offset += limit
results = service.assets.query.all_assets(
offset=offset,
limit=limit,
order_by=AssetsOrderByInput.HOSTNAME,
filter_asset_state=AssetStateFilter.ALL
)
assets.extend(results.assets)
Delete an Asset๐
from taegis_sdk_python import GraphQLService
service = GraphQLService()
results = service.assets.mutation.delete_assets([
"asset-uuid-here"
])
print(results)
Get an Asset Count๐
from taegis_sdk_python import GraphQLService
service = GraphQLService()
results = service.assets.query.asset_count_group_by_endpoint_type()
print(results)
Query by Host IDs๐
from taegis_sdk_python import GraphQLService
service = GraphQLService()
results = service.assets.query.assets_by_host_ids([
"host/sensor-id-here"
])
print(results)
Audits๐
Query Audits๐
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.audits.types import AllAuditsInput
service = GraphQLService()
results = service.audits.query.all_audits(AllAuditsInput(
offset=0,
limit=1,
))
print(results)
Search Audits๐
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.audits.types import AuditSearchInput
service = GraphQLService()
results = service.audits.query.search_audits(AuditSearchInput(
offset=0,
limit=1,
email="name@your-domain.com",
application="alerts"
))
print(results)
Collector๐
Create a Collector๐
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.collector.types import ClusterInput, NetworkInput
service = GraphQLService()
results = service.collector.mutation.create_cluster(ClusterInput(
name="sample-collector",
description="a collector created from a script!",
network: NetworkInput(dhcp=True, hostname="sample-collector-host")
))
print(results)
Query Collectors๐
from taegis_sdk_python import GraphQLService, build_output_string
from taegis_sdk_python.services.collector.types import Cluster
service = GraphQLService()
with service(
# not all users have permission to endpoint credentials
# we remove the field using the function so that updates
# to the schema don't interfere
output=build_output_string(Cluster).replace(" credentials ", " ")
):
results = service.collector.query.get_all_clusters(role="collector")
print(results)
Note
Some users may need to remove credentials from the endpoints section.
Cases๐
Query Cases๐
from taegis_sdk_python import GraphQLService
service = GraphQLService()
results = service.investigations.query.investigations_search(
page=1,
per_page=3,
query="""
type in ('Security Investigation')
"""
)
print(results)
Create a Case๐
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.investigations.types import InvestigationInput
service = GraphQLService()
results = service.investigations.mutation.create_investigation(InvestigationInput(
description="Testing",
status="Open",
contributors=[],
keyfindings="Test Keyfindings",
tags=[],
genesis_alerts=[],
genesis_events=[],
alerts=[],
events=[],
auth_credentials=[],
))
print(results)
Threat Intelligence๐
Download Available Threat Intel Indicator Lists๐
Use the following request to retrieve a list of Threat Intel indicator lists available for download. The list contains a link to each list which can be used directly without authentication, as authentication is built into the URL.
from taegis_sdk_python import GraphQLService
import requests
from pathlib import Path
from pprint import pprint as pp
service = GraphQLService()
response = requests.get(
f"{service.core.sync_url}/intel-requester/ti-list/latest",
headers=service.headers,
)
data = response.json()
len(data)
for ti in data:
with requests.get(
ti.get("link"),
allow_redirects=True
) as r:
file_path = Path(ti.get("name"))
file_path.parent.mkdir(parents=True, exist_ok=True)
with file_path.open("wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
Download Watchlist Indicators by Type๐
The following CTU threat intelligence indicator feeds have been identified as high-confidence lists and therefore may be retrieved using the threat watchlist TI API endpoint.
CTU Botnet Indicators IP List - MSSCTU Threat Group Indicators IP List - MSSThird Party Threat Group Indicators IP List - MSSCTU Botnet Indicators Domain List - MSSCTU Threat Group Indicators Domain List - MSSThird Party Threat Group Indicators Domain List - MSS
MSS lists relate to threat activity containing high fidelity indicators suitable for automated blocking and detection. Run the following script to retrieve the domain watchlist:
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.threat.types import ThreatParentType
service = GraphQLService()
results = service.threat.query.threat_watchlist(ThreatParentType.DOMAIN)
print(results)
Download Latest Threat Intel Publications๐
from taegis_sdk_python import GraphQLService
service = GraphQLService()
results = service.threat.query.threat_latest_publications(from_=0, size=3)
print(results)
Threat Intelligence by Indicator๐
It is possible to use the threat intelligence API to retrieve CTU threat intelligence reports, threat groups, and/or associated malware families for a respective indicator (if the indicator has a relationship to the aforementioned threat objects in the dataset).
from taegis_sdk_python import GraphQLService
service = GraphQLService()
results = service.threat.query.threat_indicator_intelligence("vitl.tk")
print(results)
Users๐
Inviting a User into a Tenant๐
Part 1: Invite Requirements๐
There are four primary values required to invite a user into a tenant:
access_token: XDR access token obtained by following the API Authentication steps.
tenant_id: The ID of the tenant that this user is to be invited into.
Note
The access_token you are using to make the invite request must have permission to invite users within the target tenant (tenant_id).
email_address: The email address of the user you wish to invite into the tenant.
role_id: The ID of the role that you wish to assign to the invited user. Such as Tenant Admin (ba0fdcbd-e87d-4bdd-ae7d-ca6118b25068 ) or Tenant Analyst (a4903f9f-465b-478f-a24e-82fa2e129d2e).
Part 2: Invite Request๐
Role IDs:
- Tenant Analystโ
a4903f9f-465b-478f-a24e-82fa2e129d2e - Tenant Adminโ
ba0fdcbd-e87d-4bdd-ae7d-ca6118b25068 - Tenant Auditorโ
ace1cae4-59fd-4fd1-9500-40077dc529a7 - Tenant Responderโ
a72dace7-4536-4dbc-947d-015a8eb65f4d
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.users.types import TDRUserInviteInput
service = GraphQLService()
results = service.users.mutation.invite_tdruser(TDRUserInviteInput(
email="invitee_email_address",
role_id="invitee_role_id"
))
print(results)
Retrieve a User๐
Part 1: Retrieval Requirements๐
To retrieve details of a user, the following three values are required:
access_token: XDR access token obtained by following the API Authentication steps.
user_id The unique ID of the user whose details you want to retrieve.
tenant_id: The ID of the tenant that the target user is a member of.
Note
The access_token you are using to to make the retrieval request must have permission to read users within the target tenant (tenant_id).
Part 2: Retrieval Request๐
Retrieving existing user details is done with a graphql request to the users API calling the tdruser query.
from taegis_sdk_python import GraphQLService
service = GraphQLService()
results = service.users.query.tdruser('user_id')
print(results)
Update a User๐
Part 1: User Update Requirements๐
To update details of a user the following three values are required:
access_token: XDR access token obtained by following the API Authentication steps.
user_id The unique ID of the user who you wish to make updates to (the target user).
tenant_id: The ID of the tenant that the target user is a member of.
Part 2: Update User Request๐
User updates are made via a graphql request to the users API calling the updateTDRUser mutation containing a patch for the fields to be updated on the target user.
Note
The access_token you are using to make the update request must have permission to update users within the target tenant (tenant_id).
There are a number of variables available to set within a patch to update details of a user. Fields currently available to patch include:
given_namefamily_namephone_numbersecondary_phone_number
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.users.types import TDRUserUpdateInput
service = GraphQLService()
results = service.users.mutation.update_tdruser('user_id', TDRUserUpdateInput(
phone_number="+10000000000",
secondary_phone_number="+000000000000"
))
print(results)
Revoke a Userโs Access๐
Part 1: Revocation Requirements๐
Revocation of a userโs access is performed by removing a userโs role assignments, once a user has 0 role assignments within a tenant they have no further access to that tenant. If a user has no role assignments within any tenant their account is marked as deactivated and the user is prevented from logging in to XDR.
The following values are required to revoke a userโs role assignment:
access_token: XDR access token obtained by following the API Authentication steps.
user_id: The unique ID of the user who you wish to revoke a role assignment (the target user).
tenant_id: The ID of the tenant that the target user is a member of and for which the role is to be revoked.
role_id: The ID of the role for which the assignment is to be revoked.
Part 2: Remove Role Assignment Request๐
Role assignment removals are made with a graphql request to the users API calling the removeTDRUserRoles mutation.
Note
The access_token you are using to make the role removal request must have permission to update users within the target tenant (tenant_id).
Role IDs:
- Tenant Analystโ
a4903f9f-465b-478f-a24e-82fa2e129d2e - Tenant Adminโ
ba0fdcbd-e87d-4bdd-ae7d-ca6118b25068 - Tenant Auditorโ
ace1cae4-59fd-4fd1-9500-40077dc529a7 - Tenant Responderโ
a72dace7-4536-4dbc-947d-015a8eb65f4d
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.users.types import TDRUserUpdateInput
service = GraphQLService()
results = service.users.mutation.remove_tdruser_roles('user_id', roles=["role_id"])
print(results)
Search Users๐
Part 1: Search Requirements๐
To search users the following two values are required:
access_token: XDR access token obtained by following the API Authentication steps.
tenant_id: The ID of the tenant in which to search users.
Note
The access_token you are using to to make the retrieval request must have permission to read users within the target tenant (tenant_id).
Part 2: Search Request๐
User searches are made with a graphql request to the users API calling the tdrUsersSearch query.
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.users.types import TDRUserUpdateInput
service = GraphQLService()
results = service.users.query.tdr_users_search(TDRUsersSearchInput(
tenant_status="!Deactivated"
))
print(results)
Register a Pre-Verified User๐
A new Users API has been added to allow Partners to add new users to their tenants without following the standard invite process and email notification for registration.
Part 1: Register User Requirements๐
- Valid SSO Connection on the Partner or the child tenant the user will be assigned to.
- User email domain must match the SSO Connection.
- Only available to Partners and their tenants, not Secureworks tenants.
- New Tenant Admin users must go through the standard invite process and will not work in this API.
There are four primary values required to register a user into a tenant:
access_token: XDR access token obtained by following the API Authentication steps.
tenant_id: The ID of the tenant that this user is to be registered in.
Note
The access_token you are using to make the register request must have permission to createPreRegisteredUser users within the target tenant (tenant_id). A TenantAdmin role has this permission.
email_address: The email address of the user you wish to register in the tenant.
role_id: The ID of the role that you wish to assign to the registered user, such as Tenant Auditor (ace1cae4-59fd-4fd1-9500-40077dc529a7 ) or Tenant Analyst (a4903f9f-465b-478f-a24e-82fa2e129d2e).
Part 2: Register User Request๐
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.users.types import PartnerRegistrationInput
service = GraphQLService()
results = service.users.mutation.register_partner_user(PartnerRegistrationInput(
email="user_email_address",
role_id="role_id",
role_expires_at="role_expiration_time",
language="user_preferred_language",
given_name="user_first_name",
family_name="user_last_name",
phone_number="user_phone_number",
timezone="user_timezone",
))
print(results)
Tenants๐
Retreive Tenants๐
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.tenants.types import TenantsQuery
service = GraphQLService()
results = service.tenants.query.tenants(TenantsQuery(
max_results=10
))
print(results)
Paginate Tenants๐
Page Num๐
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.tenants.types import TenantsQuery
service = GraphQLService()
results = service.tenants.query.tenants(TenantsQuery(
max_results=10
))
tenants = results.results
for page in range(2, 4 + 1):
results = service.tenants.query.tenants(TenantsQuery(
max_results=10,
page_num=page
))
tenants.extend(results.results)
print(len(tenants))
Cursor๐
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.tenants.types import TenantsQuery
service = GraphQLService()
results = service.tenants.query.tenants(TenantsQuery(
max_results=10
))
tenants = results.results
for _ in range(3):
results = service.tenants.query.tenants(TenantsQuery(
max_results=10,
cursor_pos=results.cursor_pos
))
tenants.extend(results.results)
print(len(tenants))
Filter Tenants๐
You can mix and match filters. You can use any (or all of the filters):
- name
- ids
- forHierarchies
- environmentFilter
- labelFilter
- withService
- withServices
- withPartnerSubscription
- withPartnerSubscriptions
- partnership
- createdTimeFilter
- modifiedTimeFilter
- isPartner
- withSupport
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.tenants.types import (
TenantsQuery,
EnvironmentFilter,
LabelFilter,
TimeFilter,
)
service = GraphQLService()
results = service.tenants.query.tenants(
TenantsQuery(
max_results=10,
ids=["xxxxx"],
name="%my name%",
for_hierachies=["id"],
environment_filter=EnvironmentFilter(name="EU", enabled=True),
label_filter=LabelFilter(label_name="testing"),
with_partner_subscription="%",
with_partner_subscriptions=["%"],
created_time_filter=TimeFilter(
start_time="2019-07-04T15:00:22", end_time="2019-07-06T15:00:22Z"
),
)
)
print(results)
Sort Tenants๐
Results can be sorted by any of the following:
- Id
- Name
- CreatedAt
- UpdatedAt
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.tenants.types import (
TenantsQuery,
TenantOrderField,
OrderDir,
)
service = GraphQLService()
results = service.tenants.query.tenants(
TenantsQuery(
max_results=10,
created_time_filter=TimeFilter(
start_time="2019-07-04T15:00:22", end_time="2019-07-06T15:00:22Z"
),
order_by=TenantOrderField.CREATED_AT,
order_dir=OrderDir.DESC,
)
)
print(results)
Create a Tenant๐
mutation createTenant(TenantCreateInput!) creates a tenant on a partner with some restrictions:
- You cannot create a child tenant on a partner (parent tenant) that you donโt have access to.
- You cannot create a new partner tenant (unless you are a Secureworks user with the right role).
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.tenants.types import TenantCreateInput, InputTenantLabel, TenantEnvironment
service = GraphQLService()
results = service.tenants.mutation.create_tenant(TenantCreateInput(
name="New Tenant Name",
partner_tenant_id="xxxxx",
labels=[
InputTenantLabel(
name="partner_only_label_name",
value="label_value",
owner_partner_tenant_id="xxxxx"
),
],
environments=[
TenantEnvironment.EU,
TenantEnvironment.PILOT,
]
))
print(results)
Notes๐
partnerTenantIDis the parentโs partner tenant ID. As the name indicates, you must have access to that tenant (asTenantAdmin) to be able to create child tenants.- You can restrict the audience for
labelsusing theowner_parent_tenant_id. In the example above,partner_only_label_nameis only visible to tokens that can access tenantxxxxx. You can only set label ownership to the parent tenant for now. environmentscan be used to set a list of environments where you want this child tenant enabled at; in this example the new tenant is enabled inEUandpilot.
Rename a Tenant๐
To rename a tenant, use mutation updateTenant(TenantUpdateInput):
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.tenants.types import TenantUpdateInput
service = GraphQLService()
results = service.tenants.mutation.update_tenant(
tenant_id="xxxxx",
update_input=TenantUpdateInput(
name="Updated Tenant Name",
),
)
print(results)
Manage Tenant Environments๐
When tenants are created, they are enabled in at least one environment. To change that environment use mutation updateTenant(TenantUpdateInput):
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.tenants.types import TenantUpdateInput, TenantEnvironmentUpdateInput
service = GraphQLService()
results = service.tenants.mutation.update_tenant(
tenant_id="xxxxx",
update_input=TenantUpdateInput(
environments=[
TenantEnvironmentUpdateInput(name="EU", enabled=False),
TenantEnvironmentUpdateInput(name="US2", enabled=True),
],
),
)
print(results)
Tip
You can enable and/or disable several environments in a single request. A tenant is considered active if itโs enabled in at least one environment.
Disable Tenant๐
To disable a tenant, you can either disable it in all its environments or simply set the field "disable" : true. Both options yield the same result.
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.tenants.types import TenantUpdateInput, TenantEnvironmentUpdateInput
service = GraphQLService()
results = service.tenants.mutation.update_tenant(
tenant_id="xxxxx",
update_input=TenantUpdateInput(
disable=True
),
)
print(results)
Manage Tenant Expiration๐
A tenant with an expiration date is automatically disabled after a period of 60 days. The expiration date can be modified with mutation updateTenant(TenantUpdateInput):
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.tenants.types import TenantUpdateInput, TenantEnvironmentUpdateInput
service = GraphQLService()
results = service.tenants.mutation.update_tenant(
tenant_id="xxxxx",
update_input=TenantUpdateInput(
expires_at="2023-12-31T00:00:00Z"
),
)
print(results)
Clear an Expiration๐
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.tenants.types import TenantUpdateInput, TenantEnvironmentUpdateInput
service = GraphQLService()
results = service.tenants.mutation.update_tenant(
tenant_id="xxxxx",
update_input=TenantUpdateInput(
clear_expiration=True,
),
)
print(results)
Manage Tenant Labels๐
Labels can be created, updated, and deleted in tenants using the following mutations:
createTenantLabel(label_id ID!, label_input ID!: InputTenantLabel!): TenantLabel!,InputTenantLabel.owner_partner_tenant_idcan be used to establish label audience.updateTenantLabel(label_id ID!, tenant_id ID!, label_input: InputTenantLabel!): TenantLabel!deleteTenantLabel(label_id: ID!, tenant_id: ID!): TenantLabel!
Note
Some labels are restricted and cannot be modified or deleted. The Tenants API informs you if you attempt to modify a restricted label.
Create a Tenant Label๐
To create a new label on a tenant that you own:
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.tenants.types import InputTenantLabel
service = GraphQLService()
results = service.tenants.mutation.create_tenant_label(
tenant_id="xxxxx",
label_input=InputTenantLabel(
name="test",
value="value",
),
)
print(results)
Manage Partner Subscriptions๐
You can manage both available partner subscriptions and subscription assignments on child tenants with the following mutations:
createSubscription(input: NewSubscription!): Service!allows you to create a subscription that can be assigned to child tenants.updateSubscription(input: SubscriptionUpdate!): Service!lets you rename a subscription that you have access to.deleteSubscription(id: ID!): Service!removes a subscription that is not assigned. An error is returned if the subscription is already assigned.assignSubscription(tenant_id: ID!, subscription_id: ID!): Tenant!allows you to set a subscription on a partner child tenant.unassignSubscription(tenant_id: ID!, subscription_id: ID!): Tenant!allows removing a subscription from a partner child tenant.
from taegis_sdk_python import GraphQLService
service = GraphQLService()
results = service.tenants.mutation.assign_subscription(
tenant_id="xxxxx",
subscription_id="1cb04358-0e00-4ffb-abc8-14ccbc7b42b0"
)
print(results)
Notes๐
subscription_idis returned when you create a subscription. The returnedidvalue is the subscription assignment ID, which you can use to unassign the subscription.
Allow Secureworks Support๐
tenants-api can be used to enable or disable support on the partner child tenants via the following mutations:
enableTenantSupport(tenantID: ID!): Tenant!disableTenantSupport(tenantID: ID!): Tenant!
from taegis_sdk_python import GraphQLService
service = GraphQLService()
results = service.tenants.mutation.enable_tenant_support(
tenant_id="xxxxx",
)
print(results)
Single Sign-On Connections๐
Configuring a third-party identity provider for authentication to the Taegis platform is handled by the Tenants API. Only SAML providers are supported and this guide describes the steps necessary to configure a provider. In this scenario of SAML communications, the Taegis platform is the service provider.
Create an SSO Connection๐
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.tenants.types import NewSSOConnectionInput, SSOConnectionType
service = GraphQLService()
results = service.tenants.mutation.create_sso_connection(
NewSSOConnectionInput(
name="Connection Name",
type=SSOConnectionType.SAML,
domains=["email_domain"],
testers=["tester@email_domain"]
)
)
print(results)
Notes๐
-
Inputs
nameโ The name of the SSO connection to be created. Requiredtypeโ Type of connection, must besaml. Requireddomainsโ List of email domains which use this connection for authentication. Requiredtestersโ List of testers who are the early adopters for this connection. Optional
-
Outputs
idโ The internal ID of the connection.typeโ The type of connection created, should besaml.createdAtโ The time the connection was created.updatedAtโ The time of last update.externalNameโ The connectionโs external name. This is generated.externalIDโ The connectionโs external ID. This is generated.domainsโ The list of configured email domains using this connection.testersโ The list of users testing this connection.statusโ The status of this connection; should report asDraftat this step; other possible values areTesting,Enabled, orDisabled.ssoConnectionParameterspostBackURLโ Used to configure on the identity provider. This is the callback that receives the SAML assertion from the identity provider, also known asassertion consumer service.entityIDโ Identifies the Taegis connection to the identity provider.
The postBackURL and entityID are usually configured on the SAML identity provider prior to continuing to the next step.
Get SSO Configuration๐
The getSSOConnectionConfig query retrieves information that is required by the SAML service provider (Taegis). This step is typically done as pre-confirmation to make sure configuration settings are correct.
from taegis_sdk_python import GraphQLService
service = GraphQLService()
results = service.tenants.query.get_sso_connection_config(
metadata_url="metadata_url",
)
print(results)
Notes๐
Inputs๐
metadataURLspecifies the identity providerโs metadata URL. This is optional and mutually exclusive withcert.certis the signing certificate for the identity provider. This is not strictly necessary since the other data is available and is provided as a convenience. This is optional and mutually exclusive withmetadataURL.
Outputs๐
expiresAtโ Expiration date for the signing certificate.notBeforeโ Start date the signing certificate is valid from.issuerโ Entity that issued the signing certificate.subjectโ Entity the signing certificate was issued for.ssoConnectionConfigurationsamlConnectionConfigurationis returned ifmetadataURLis specified.signInEndpointis the endpoint Taegis invoked to authenticate the SSO user.signingCertis the identity providerโs signing certificate extracted from the metadata URL.
Complete Initial SSO Connection Configuration๐
To complete the initial connection configuration, either the identity providerโs metadata URL or the combination of the signing certificate and sign-in endpoint are required. On successful completion the connection transitions to Testing status.
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.tenants.types import UpdateSSOConnectionInput, ConnectionConfiguration, SAMLSSOConfiguration
service = GraphQLService()
results = service.tenants.mutation.update_sso_connection(
UpdateSSOConnectionInput(
id="connection_id",
connection_configuration=ConnectionConfiguration(
saml_configuration=SAMLSSOConfiguration(
metadata_url="metadata_url",
# or #
signing_cert_name="certificate_name",
signing_cert="certificate",
sign_in_endpoint="sign_in_endpoint"
)
)
)
)
print(results)
Notes๐
Inputs๐
idโ Internal ID of the connection. RequiredsamlConfigurationmetadataURLโ URL that allows downloads of the identity providerโs configuration. This is optional and if provided, thesigningCertinput is ignored.signingCertโ The identity providerโs signing certificate. This is optional and if specified andmetadataURLis not,signInEndpointmust also be specified.signInEndpointโ URL to be used to authenticate the SSO user. This is optional and must be specified ifsigningCertis used.signingCertNameโ File name of the signing certificate; used to historically identify the last certificate used, but not used to retrieve the certificate from the userโs local file system. Optional
Outputs๐
idโ Internal ID of the connection.nameโ The connection name.externalNameโ External name used by Taegis to handle SSO authentication.externalIDโ External ID used by Taegis to handle SSO authentication.createdAtโ The time the connection was created.updatedAtโ The time the connection was last updated.typeโ The type of SSO connection; only SAML is currently supported.statusโ Connection status, which transitions toTestingon successful completion of the request.domainsโ The email domains that authenticate using this connection.testersโ The list of testers.cerNameโ If specified in the update request, the certificate file name.notBeforeโ The time the signing certificate is valid from.expiresAtโ The time the signing certificate expires.issuerโ The entity that issued the signing certificate.subjectโ The entity the signing certificate was issued to.ssoConnectionParametersโ The service providerโs (Taegis) SAML attributes.postBackURLโ The endpoint that receives the SAML assertion response from the identity provider.entityIDโ Identifier for the Taegis SAML entity.metadataURLโ URL the identity provider can use in the future to download Taegisโs SAML configuration if updates are needed.
ssoConnectionIDPConfigโ The identity providerโs configuration.samlConnectionConfigurationsignInEndpointโ The endpoint Taegis uses to authenticate SSO users.signingCertโ The identity providerโs signing certificate.
Updating SSO Connections๐
After testing is complete, the SSO connection must be placed in Enabled status to make it available to the registered email domains. Other attributes may also need to be adjusted. For example, a new signing certificate when the configured one is set to expire.
from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.tenants.types import UpdateSSOConnectionInput, SSOConnectionStatus
service = GraphQLService()
results = service.tenants.mutation.update_sso_connection(
UpdateSSOConnectionInput(
id="connection_id",
status=SSOConnectionStatus.ENABLED
)
)
print(results)
Notes๐
Inputs๐
idโ Internal ID of the connection. Requiredstatusโ Updates the status of the connection. Valid values areEnabled,Disabled, orTesting. OptionaladdTestersโ A string array of new testers to add. OptionalremoveTestersโ A string array of testers to remove. OptionaladdDomainsโ A string array of email domains to add. OptionalremoveDomainsโ A string array of email domains to remove. OptionalsamlConfigurationmetadataURLโ URL that allows downloads of the identity providerโs configuration. If provided, thesigningCertinput is ignored. OptionalsigningCertโ The identity providerโs signing certificate. If specified butmetadataURLis not,signInEndpointmust also be specified. OptionalsignInEndpointโ URL to be used to authenticate the SSO user. Optional, but must be specified ifsigningCertis used.signingCertNameโ File name of the signing certificate. Used to historically identify the last certificate used and not used to retrieve the certificate from the userโs local file system. Optional
Outputs๐
idโ Internal ID of the connection.nameโ The connection name.externalNameโ External name used by Taegis to handle SSO authentication.externalIDโ External ID used by Taegis to handle SSO authentication.createdAtโ The time the connection was created.updatedAtโ The time the connection was last updated.typeโ The type of SSO connection. Only SAML is currently supported.statusโ Connection status; this should transition toTestingon successful completion of this request.domainsโ The email domains that authenticate using this connection.testersโ The list of testers.cerNameโ If specified in the update request, this is the certificate file name.notBeforeโ The time the signing certificate is valid from.expiresAtโ The time the sigining certificate expires.issuerโ The entity which issued the signing certificate.subjectโ The entity the signing certificate was issued to.ssoConnectionParametersโ The service providerโs (Taegis) SAML attributes.postBackURLโ The endpoint that receives the SAML assertion response from the identity provider.entityIDโ Identifier for the Taegis SAML entity.metadataURLโ URL the identity provider can use in the future to download Taegisโs SAML configuration if updates are needed.
ssoConnectionIDPConfigโ The identity providerโs configuration.samlConnectionConfigurationsignInEndpointโ The endpoint Taegis uses to authenticate SSO users.signingCertโ The identity providerโs signing certificate.
Delete SSO Connections๐
from taegis_sdk_python import GraphQLService
service = GraphQLService()
results = service.tenants.mutation.delete_sso_connection(
connection_id="connection_id"
)
print(results)
Notes๐
Inputs๐
connectionIDโ Internal ID of the connection. Required
Outputs๐
idโ Internal ID of the connection.nameโ The connection name.typeโ The type of SSO connection; only SAML is currently supported.statusโ Connection status; transitions toTestingon successful completion of the request.externalNameโ External name used by Taegis to handle SSO authentication.externalIDโ External ID used by Taegis to handle SSO authentication.domainsโ Email domains that authenticate using this connection.testersโ The list of testers.
Countermeasures๐
The following endpoints can be accessed using the XDR Countermeasures API to retrieve CTU information:
/โ Returns rulesets. Example:https://api.ctpx.secureworks.com/intel-requester//ti-listโ Returns all Threat Indicator List versions released by the CTU. Example:https://api.ctpx.secureworks.com/intel-requester/ti-list/ti-list/latestโ Returns only the latest versions of Threat Indicator Lists released by the CTU. Example:https://api.ctpx.secureworks.com/intel-requester/ti-list/latest
By default, the results from these endpoints are not url escaped. If you need to display these results in a web page, please use the query parameter ?html=true.
Threat Indication List endpoints by default return filtered lists (MSS). If you need all the lists or only raw lists, please use the filter query parameter.*
?filter=all => all lists?filter=raw => raw lists
*omit the query param or ?filter=mss for mss lists.
Get List of Countermeasures๐
from taegis_sdk_python import GraphQLService
import requests
import tarfile
service = GraphQLService()
response = requests.get(
f"{service.core.sync_url}/intel-requester/",
headers=service.headers,
)
data = response.json()
print(data)
for cm in data:
name = cm.get("name").split("/")[1]
with requests.get(
cm.get("link"),
allow_redirects=True
) as r:
with open(name, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)