XDR Python SDK Examples
Alerts ⫘
Search Alerts ⫘
The following example performs a search of alerts using the query language and returns a number of common fields of interest. This query example returns High/Critical alerts 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 Alerts by ID ⫘
The following query can be used to retrieve an alert 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 Alerts by ID ⫘
Use the following to resolve a list of Alerts by their IDs. This accepts a list of one or more alert IDs, the reason for resolving, and the resolution_status to label the alerts 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 Alert Data ⫘
Use the following query to retrieve alert 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)
Alerts Pagination ⫘
There are two primary methods of pagination. The first allows for retrieval of up to 10,000 alerts. The other allows for retrieval of up to 1,000,000 alerts.
Under 10k Alerts ⫘
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 Alerts ⫘
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 Alerts 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.
Investigations ⫘
Query Investigations ⫘
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 an Investigation ⫘
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 - MSS
CTU Threat Group Indicators IP List - MSS
Third Party Threat Group Indicators IP List - MSS
CTU Botnet Indicators Domain List - MSS
CTU Threat Group Indicators Domain List - MSS
Third 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_name
family_name
phone_number
secondary_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 ⫘
partnerTenantID
is 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
labels
using theowner_parent_tenant_id
. In the example above,partner_only_label_name
is only visible to tokens that can access tenantxxxxx
. You can only set label ownership to the parent tenant for now. environments
can be used to set a list of environments where you want this child tenant enabled at; in this example the new tenant is enabled inEU
andpilot
.
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_id
can 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_id
is returned when you create a subscription. The returnedid
value 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 asDraft
at this step; other possible values areTesting
,Enabled
, orDisabled
.ssoConnectionParameters
postBackURL
— 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 ⫘
metadataURL
specifies the identity provider’s metadata URL. This is optional and mutually exclusive withcert
.cert
is 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.ssoConnectionConfiguration
samlConnectionConfiguration
is returned ifmetadataURL
is specified.signInEndpoint
is the endpoint Taegis invoked to authenticate the SSO user.signingCert
is 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. RequiredsamlConfiguration
metadataURL
— URL that allows downloads of the identity provider’s configuration. This is optional and if provided, thesigningCert
input is ignored.signingCert
— The identity provider’s signing certificate. This is optional and if specified andmetadataURL
is not,signInEndpoint
must also be specified.signInEndpoint
— URL to be used to authenticate the SSO user. This is optional and must be specified ifsigningCert
is 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 toTesting
on 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.samlConnectionConfiguration
signInEndpoint
— 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. OptionalsamlConfiguration
metadataURL
— URL that allows downloads of the identity provider’s configuration. If provided, thesigningCert
input is ignored. OptionalsigningCert
— The identity provider’s signing certificate. If specified butmetadataURL
is not,signInEndpoint
must also be specified. OptionalsignInEndpoint
— URL to be used to authenticate the SSO user. Optional, but must be specified ifsigningCert
is 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 toTesting
on 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.samlConnectionConfiguration
signInEndpoint
— 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 toTesting
on 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)