コンテンツにスキップ

XDR SDK for Python のはじめ方🔗

注意

お客様のテナントリージョンに応じて、以下のリージョンまたは環境識別子を使用してください。

  • US1 または charlie または production は https://ctpx.secureworks.com/ 用
  • US2 または delta は https://delta.taegis.secureworks.com/ 用
  • US3 または foxtrot は https://foxtrot.taegis.secureworks.com/ 用
  • EU1 または echo は https://echo.taegis.secureworks.com/ 用
  • EU2 または golf は https://golf.taegis.secureworks.com/ 用

注意

production は、Tenants API を使用して複数のテナントに対してAPIコールを繰り返したい子テナントを持つパートナーに便利です。Tenants API は charlieUS1 ではなく production 識別子を使用しますが、SDKは正しいリージョンに誘導されます。

Taegis SDK for Python のインストール🔗

Python 3.8+ がすでにインストールされていることを前提に、Taegis SDK for Python をインストールします。

python -m pip install taegis-sdk-python

認証🔗

インストール時に、お客様の Secureworks® Taegis™ XDR アカウントで認証を求められます。これはパスワードとMFAトークンが必要ですが、組織がSSO認証を登録している場合は除きます。SSOが有効な場合は、デバイスコード認証リンクが表示されます。

詳細は Taegis SDK for Python での認証 をご覧ください。

使用例🔗

from taegis_sdk_python import GraphQLService

from pprint import pprint as pp

service = GraphQLService()

results = service.subjects.query.current_subject()

pp(results)

SDK の探索🔗

このSDKは、Pythonの組み込み help を活用することを前提に設計されています。GraphQLService オブジェクト構造内の任意のオブジェクトに対して help を使用することで、利用可能な内容や呼び出し方法を確認できます。helpメニューは入力型を判断するのに非常に役立ちます。各サービスは独立しているため、例えば SearchRequestInput のような入力が必要な場合は、taegis_sdk_python.services.<service>.types 配下で見つけることができます。

from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.alerts.types import SearchRequestInput

service = GraphQLService()

# Find available services (Service Endpoints)
help(service)
# Find available service queries (or mutations or subscriptions)
help(service.alerts.query)
help(service.alerts.mutation)
help(service.alerts.subscription)
# Reference documentation on specific endpoint
help(service.alerts.query.alerts_service_search)
# Help on an Input variable
help(SearchRequestInput)
# service
class GraphQLService(builtins.object)
 |  GraphQLService(*, environment: Optional[str] = None, tenant_id: Optional[str] = None, environments: Optional[Dict[str, str]] = None, gateway: Optional[str] = None)
...
 |  agent
 |      Events Service Endpoint.
 |
 |  alerts
 |      Alerts2 Service Endpoint.
 |
 |  assets
 |      Assets
...
# Alerts Query
class TaegisSDKAlertsQuery(builtins.object)
 |  TaegisS
...
 |  alerts_service_aggregate_alerts_by_severity(self, in_: 'Optional[AggregateAlertsBySeverityInputInput]' = None) -> 'AlertsAggregateResponse'
 |      Pull alert severity aggregates based on `group_by` parameters: domain, watchlist, hostname, detector, user..
 |
 |  alerts_service_alerts_dashboard_triage(self, in_: 'Optional[TriageDashboardInputInput]' = None) -> 'TriageDashboardOutput'
 |      None.
 |
 |  alerts_service_poll(self, in_: 'Optional[PollRequestInput]' = None) -> 'AlertsResponse'
 |      Poll for results for a specific `search
...
# SearchRequestInput
class SearchRequestInput(builtins.object)
 |  SearchRequestInput(cql_query: Optional[str] = None, offset: Optional[int] = None, limit: Optional[int] = None) -> None
...

注: 出力は冗長性のため省略されています。

コンテキストマネージャ🔗

サービスオブジェクトはコンテキストマネージャとしても機能し、APIコール時にデフォルト値を一時的に上書きするのに役立ちます。これには environmenttenant_idoutputaccess_token などのフィールドが含まれます。

from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.alerts.types import SearchRequestInput

service = GraphQLService()

with service(
    environment="US2",
    tenant_id="00000",
    output="""
        reason
        alerts {
            total_results
            list {
                id
                tenant_id
                metadata {
                    title
                    severity
                }
                status
            }
        }
    """,
):
    result = service.alerts.query.alerts_service_search(SearchRequestInput(
        offset=0,
        limit=10,
        cql_query="""
        FROM alert
        WHERE
            severity >= 0.6
        EARLIEST=-1d
        """
    ))

テナントコンテキストの変更🔗

from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.investigations.types import InvestigationsV2Arguments

service = GraphQLService()

# specify the output fields, and start the service context
with service(tenant_id="00000"):
    result = service.investigations2.query.investigations_v2(InvestigationsV2Arguments(
        page=1,
        per_page=3,
        cql="WHERE deleted_at IS NOT NULL EARLIEST=-90d"
    ))
pp(result)

環境の変更🔗

from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.investigations.types import InvestigationsV2Arguments

service = GraphQLService()

# specify the output fields, and start the service context
with service(environment="US2"):
    result = service.investigations2.query.investigations_v2(InvestigationsV2Arguments(
        page=1,
        per_page=3,
        cql="WHERE deleted_at IS NOT NULL EARLIEST=-90d"
    ))
pp(result)

既存のアクセストークンを使用する🔗

from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.investigations.types import InvestigationsV2Arguments

service = GraphQLService()

# specify the output fields, and start the service context
with service(access_token="<your access token>"):
    result = service.investigations2.query.investigations_v2(InvestigationsV2Arguments(
        page=1,
        per_page=3,
        cql="WHERE deleted_at IS NOT NULL EARLIEST=-90d"
    ))
pp(result)

GraphQL 出力の間引き🔗

GraphQL を使用する利点の一つは、返却されるフィールドを定義できることです。デフォルトでは、GraphQL の知識がほとんどない、または全くない方でも始められるよう、APIコールで全てのフィールドを提供していますが、特定のアプリケーションやレポート用途では不要な場合もあります。

これを支援するために、build_output_string というユーティリティを用意しています。これは、返却型の全ての可能なフィールドを含む出力オブジェクトの文字列表現を返します。これを参考にして独自の出力を作成したり、不要なフィールドを削除したりできます。

from taegis_sdk_python import build_output_string
from taegis_sdk_python.services.alerts.types import AlertsResponse

print(build_output_string(AlertsResponse))
reason search_id status alerts { previous_offset total_parts list { reference_details { reference {
description url type } } parent_tenant_id entities { entities relationships { relationship to_entity
from_entity type } } sensor_types suppression_rules { id version } resolution_history { timestamp {
nanos seconds } user_id status num_alerts_affected id reason } enrichment_details {
business_email_compromise { user_name source_address_geo_summary { country { confidence iso_code
geoname_id code } city { confidence locale_names { record { value key } } geoname_id name } location {
timezone latitude longitude us_metro_code radius metro_code gmt_offset } asn { autonomous_system_no
autonomous_system_org } continent { code geoname_id } } source_address }
...

注: 出力は冗長性のため省略されています。

サービスオブジェクトは、output に新しいGraphQL出力フィールドを割り当ててコンテキストマネージャとして呼び出すことができます。同じオブジェクトが返されますが、定義されていないフィールドには None が割り当てられます。

from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.alerts.types import SearchRequestInput

from pprint import pprint as pp

service = GraphQLService()
with service(output="search_id alerts { list { id status metadata { title severity confidence } } }")
    results = service.alerts.query.alerts_service_search(
        SearchRequestInput(
            offset=0,
            limit=10,
            cql_query="""
            FROM alert
            WHERE
                severity >= 0.6
            EARLIEST=-1d
            """,
        )
    )
pp(results)

カスケーディングコンテキスト🔗

Taegis SDK for Python サービスはカスケーディングコンテキストを扱うことができます。service コンテキストマネージャの各呼び出しは、スタックごとにコンテキストを上書きします。主なユースケースは、より大きなコンテキスト内で特定のAPIコールの output のみを変更したい場合です。新しいレベルは一時的に以前のコンテキスト定義を上書きし、現在のコンテキストを抜けると以前の定義が再び有効になります。

注記: 以下の例はコンテキストマネージャの説明用です。APIコールの組み合わせ自体は有用でない場合があります。

from taegis_sdk_python import GraphQLService
from taegis_sdk_python.services.investigations2.types import InvestigationsV2Arguments
from taegis_sdk_python.services.alerts.types import SearchRequestInput

service = GraphQLService()

with service(environment="US2", tenant_id="00000"):

    # Context
    #    environment: US2
    #    tenant_id: 00000

    with service(output="investigations { id alertsEvidence { id } }")

        # Context
        #    environment: US2
        #    tenant_id: 00000
        #    output: investigations { id alertsEvidence { id } }

        investigation_results = service.investigations2.query.investigations_v2(InvestigationsV2Arguments(
            page=1,
            per_page=3,
            cql="WHERE deleted_at IS NOT NULL EARLIEST=-90d"
        ))

    # Context
    #    environment: US2
    #    tenant_id: 00000

    alert_ids = [
        alert.id
        result for result in investigation_results
        alert for alert in result.alerts_evidence
    ]

    with service(output="alerts { list { id metadata { title } status } }"):

        # Context
        #    environment: US2
        #    tenant_id: 00000
        #    output: alerts { list { id metadata { title } status } }

        alert_results = service.alerts.query.alerts_search_search(SearchRequestInput(
            offset=0,
            limit=10000,
            cql_query=f"FROM alert WHERE resource_id IN ('{'\',\''.join(alert_ids)]}')"
        ))

    # Context
    #    environment: US2
    #    tenant_id: 00000

    # may be useful for users/applications that have access to a parent/child tenant relationship
    with service(tenant_id="00001", output="alerts { list { id metadata { title } status } }"):

        # Context
        #    environment: US2
        #    tenant_id: 00001
        #    output: alerts { list { id metadata { title } status } }

        alert_results = service.alerts.query.alerts_search_search(SearchRequestInput(
            offset=0,
            limit=10000,
            cql_query=f"FROM alert WHERE resource_id IN ('{'\',\''.join(alert_ids)]}')"
        ))

        with service(environment="US1", output="email"):

            # Context
            #    environment: US1
            #    tenant_id: 00001
            #    output: email

            user = service.users.query.current_tdruser()

        # Context
        #    environment: US2
        #    tenant_id: 00001
        #    output: alerts { list { id metadata { title } status } }

    # Context
    #    environment: US2
    #    tenant_id: 00000

# Context is now completely cleared

任意のクエリ🔗

提供されているメソッドとは異なるAPIコールを実行したい場合や、SDKがサポートしていないAPIコールを実行したい場合は、自分でクエリ/ミューテーション/サブスクリプションを作成できます。サービスによっては設定が異なる場合があるため、利用したいサービスエンドポイントを指定することを推奨します。

  • execute_query
  • execute_mutation
  • execute_subscription
from taegis_sdk_python import GraphQLService

from pprint import pprint as pp

service = GraphQLService()

results = service.alerts.execute_query(
    endpoint="alertsServiceSearch",
    variables={
        "in": {
            "limit": 3,
            "offset": 0,
            "cql_query": """
            FROM alert
            WHERE
                severity >= 0.6
            EARLIEST=-1d
            """
        }
    },
    output="""
        search_id
        alerts {
            list {
                id
                tenant_id
                metadata {
                    title
                    severity
                }
                status
            }
        }
    """
)
pp(results)

任意のミューテーション🔗

results = service.core.execute_mutation(
    "createInvestigation",
    variables={
        "investigation": {
            "description": "SDK Test Investigation",
            "key_findings": "This is a test.",
            "priority": 1
        }
    },
    output="""
    id
    created_at
    created_by_user {
        id
        given_name
        family_name
    }
    description
    key_findings
    """
)
print(results)

生クエリ🔗

独自の生のGraphQL文字列を実行することも可能です。これにより最大限の柔軟性が得られますが、ガードレールは最小限となります。

  • execute
  • subscribe
from taegis_sdk_python import GraphQLService

from pprint import pprint as pp

service = GraphQLService()

results = service.investigations.execute("""
    query investigationsStatusCount
    {
        investigationsStatusCount
        {
            open closed active awaiting_action suspended total
        }
    }
""")
pp(results)

ヘルパー🔗

build_output_string ユーティリティ🔗

build_output_string というユーティリティがあります。これは、返却型の全ての可能なフィールドを含む出力オブジェクトの文字列表現を返します。

from taegis_sdk_python import build_output_string
from taegis_sdk_python.services.alerts.types import AlertsResponse

print(build_output_string(AlertsResponse))

_build_output_query ユーティリティ🔗

完全なGraphQLクエリ文字列の作成を支援したい場合は、サービスエンドポイントの _build_output_query を呼び出すことでサポートできます。これはスキーマからクエリを構築するため、正しいサービスエンドポイントからスキーマを取得していることを確認してください。

from taegis_sdk_python import GraphQLService, build_output_string
from taegis_sdk_python.services.investigations.types import InvestigationStatusCountResponse

service = GraphQLService()
schema = service.alerts.get_sync_schema()

print(service.alerts._build_output_query(
    operation_type="query",
    endpoint="investigationsStatusCount",
    graphql_field=schema.query_type.fields.get("investigationsStatusCount"),
    output=build_output_string(InvestigationStatusCountResponse)
))

非推奨警告🔗

非推奨の入力フィールド、出力フィールド、エンドポイントは警告をログに記録するよう設定されています。詳細は Taegis SDK for Python の非推奨について をご覧ください。

例:

GraphQL Query `allInvestigations` is deprecated: 'replaced by investigationsSearch'
Output field `activity_logs` is deprecated: 'Not Supported - Use audit logs', removing from default output...
Output field `assignee` is deprecated: 'No longer supported', removing from default output...

スキーマの取得🔗

Taegis SDK for Python はスキーマを5分間キャッシュします。取得したスキーマはクエリ文字列の生成やエラーハンドリングに使用されます。時間に敏感なアプリケーションや大量のAPIコールを行う場合、この機能によりスキーマ取得にかかる時間を分散できます。GraphQLServiceschema_expiry(分単位)属性で設定可能です。スキーマはサービスごとに取得され(alertsはinvestigationsとは別にスキーマをキャッシュします)、有効期限は最初のAPIコール時にコンテキストマネージャ経由でサービスごとに設定できます。

注意

有効期限が長いとエラーハンドリングに影響する場合があります。サーバー側のスキーマがキャッシュとAPIコールの間で破壊的に変更された場合、SDKはデプロイ済みスキーマと不整合なクエリ文字列を生成・検証する可能性があります。

from taegis_sdk_python import GraphQLService

# all schemas will be cached for 15 minutes
service = GraphQLService(schema_expiry=15)

with service(schema_expiry=30): # users schema will now be cached for 30 minutes
    results = service.users.query.current_tdruser()

スキーマは service.<service>.clear_schema() メソッドでサービスごとにクリアできます。

from taegis_sdk_python import GraphQLService

service = GraphQLService()

user = service.users.query.current_tdruser() # schema will be cached for the users service

service.users.clear_schema()  # local schema will be cleared and re-fetched on next call

サブスクリプションメッセージサイズ🔗

サブスクリプションの最大メッセージサイズは、全てのサブスクリプションで max_message_size パラメータで設定できます。個別の呼び出しはコンテキストマネージャ経由で変更可能です。デフォルトは0(制限なし)です。このオプションはサブスクリプションのみに影響し、クエリやミューテーションには影響しません。

メッセージサイズに到達または超過した場合、ConnectionResetError がスローされます。

from taegis_sdk_python import GraphQLService

service = GraphQLService(max_message_size=4194304)  # sets default to 4MB

with service(max_message_size=5242880):  # sets specific API call to 5MB
    options = EventQueryOptions(
        timestamp_ascending=True,
        page_size=1000,
        max_rows=1000,
        skip_cache=True,
        aggregation_off=False,
    )
    results = service.events.subscription.event_query("FROM process EARLIEST=-1d", options=options)

try:
    results = service.events.subscription.event_query("FROM process EARLIEST=-1d", options=options)
except ConnectionResetError as exc:
    # handle error