Skip to content

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 (as TenantAdmin) to be able to create child tenants.
  • You can restrict the audience for labels using the owner_parent_tenant_id. In the example above, partner_only_label_name is only visible to tokens that can access tenant xxxxx. 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 in EU and pilot.

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 returned id 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. Required
    • type — Type of connection, must be saml. Required
    • domains — List of email domains which use this connection for authentication. Required
    • testers — 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 be saml.
    • 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 as Draft at this step; other possible values are Testing, Enabled, or Disabled.
    • ssoConnectionParameters
      • postBackURL — Used to configure on the identity provider. This is the callback that receives the SAML assertion from the identity provider, also known as assertion 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 with cert.
  • 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 with metadataURL.
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 if metadataURL 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. Required
  • samlConfiguration
    • metadataURL — URL that allows downloads of the identity provider’s configuration. This is optional and if provided, the signingCert input is ignored.
    • signingCert — The identity provider’s signing certificate. This is optional and if specified and metadataURL is not, signInEndpoint must also be specified.
    • signInEndpoint — URL to be used to authenticate the SSO user. This is optional and must be specified if signingCert 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 to Testing 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. Required
  • status — Updates the status of the connection. Valid values are Enabled, Disabled, or Testing. Optional
  • addTesters — A string array of new testers to add. Optional
  • removeTesters — A string array of testers to remove. Optional
  • addDomains — A string array of email domains to add. Optional
  • removeDomains — A string array of email domains to remove. Optional
  • samlConfiguration
    • metadataURL — URL that allows downloads of the identity provider’s configuration. If provided, the signingCert input is ignored. Optional
    • signingCert — The identity provider’s signing certificate. If specified but metadataURL is not, signInEndpoint must also be specified. Optional
    • signInEndpoint — URL to be used to authenticate the SSO user. Optional, but must be specified if signingCert 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 to Testing 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 to Testing 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)