Security Command Center API を使用してセキュリティの検出結果を一覧表示する

Security Command Center の検出結果により、プロジェクトまたは組織のアセットの潜在的なセキュリティ リスクがモデル化されます。検出結果は、常に Security Command Center の特定のアセットに関連付けられます。

このガイドでは、Security Command Center クライアント ライブラリを使用して検出結果にアクセスする方法について説明します。各検出結果は 1 つのソースに属します。ほとんどの検出器や検出プロバイダで、同じソースの検出結果が生成されます。

Security Command Center の IAM ロールは、組織レベル、フォルダレベル、またはプロジェクト レベルで付与できます。検出結果、アセット、セキュリティ ソースを表示、編集、作成、更新する権限は、アクセス権が付与されているレベルによって異なります。Security Command Center のロールの詳細については、アクセス制御をご覧ください。

始める前に

ソースを設定する前に、以下の手順を完了しておく必要があります。

ページサイズ

Security Command Center の list API は、すべてページ分けされます。各レスポンスでは、結果のページと次のページを返すためのトークンが戻されます。ページサイズは構成可能です。デフォルトのページサイズは 10 です。最小値は 1、最大値は 1, 000 に設定できます。

検出結果の保持

検出結果は少なくとも 13 か月間一覧表示またはクエリを行うことが可能です。

Security Command Center には、各検出結果のスナップショットが保存されます。検出結果のスナップショットは少なくとも 13 か月間保持されます。検出結果のスナップショットがすべて削除されると、検出結果の一覧表示や復元ができなくなります。

Security Command Center のデータ保持の詳細については、データの保持をご覧ください。

すべての検出結果を一覧表示する

gcloud

プロジェクト、フォルダ、または組織内のすべての検出結果を一覧表示するには、次のコマンドを実行します。

gcloud scc findings list PARENT_TYPE/PARENT_ID \
  --location=LOCATION

次のように置き換えます。

  • PARENT_TYPE: 検出結果を一覧表示するリソース階層のレベル。organizationsfoldersprojects のいずれかを使用します。
  • PARENT_ID: 組織、フォルダ、プロジェクトの数値 ID、または英数字のプロジェクト ID。
  • LOCATION: データ所在地が有効になっている場合は、検出結果を一覧表示する Security Command Center のロケーション。データ所在地が有効になっていない場合は、値 global を使用します。

他の例については、次のコマンドを実行します。

gcloud scc findings list --help

ドキュメントの例については、gcloud scc findings list をご覧ください。

Go

import (
	"context"
	"fmt"
	"io"

	securitycenter "cloud.google.com/go/securitycenter/apiv2"
	"cloud.google.com/go/securitycenter/apiv2/securitycenterpb"
	"google.golang.org/api/iterator"
)

// listFindings prints all findings in orgID to w. orgID is the numeric
// identifier of the organization.
func listFindings(w io.Writer, orgID string) error {
	// orgID := "12321311"
	// Instantiate a context and a security service client to make API calls.
	ctx := context.Background()
	client, err := securitycenter.NewClient(ctx)
	if err != nil {
		return fmt.Errorf("securitycenter.NewClient: %w", err)
	}
	defer client.Close() // Closing the client safely cleans up background resources.

	req := &securitycenterpb.ListFindingsRequest{
		// List findings across all sources.
		// Parent must be in one of the following formats:
		//		"organizations/{orgId}/sources/-/locations/global"
		//		"projects/{projectId}/sources/-/locations/global"
		//		"folders/{folderId}/sources/-/locations/global"
		Parent: fmt.Sprintf("organizations/%s/sources/-/locations/global", orgID),
	}
	it := client.ListFindings(ctx, req)
	for {
		result, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return fmt.Errorf("it.Next: %w", err)
		}
		finding := result.Finding
		fmt.Fprintf(w, "Finding Name: %s, ", finding.Name)
		fmt.Fprintf(w, "Resource Name %s, ", finding.ResourceName)
		fmt.Fprintf(w, "Category: %s\n", finding.Category)
	}
	return nil
}

Java


import com.google.cloud.securitycenter.v2.ListFindingsRequest;
import com.google.cloud.securitycenter.v2.ListFindingsResponse.ListFindingsResult;
import com.google.cloud.securitycenter.v2.SecurityCenterClient;
import java.io.IOException;

public class ListAllFindings {

  public static void main(String[] args) throws IOException {
    // organizationId: The source to list all findings for.
    // You can also use project/ folder as the parent resource.
    String organizationId = "google-cloud-organization-id";

    // Specify the location to list the findings.
    String location = "global";

    // The source id to scope the findings.
    String sourceId = "source-id";

    listAllFindings(organizationId, sourceId, location);
  }

  // List all findings under a given parent resource.
  public static void listAllFindings(String organizationId, String sourceId, String location)
      throws IOException {
    // Initialize client that will be used to send requests. This client only needs to be created
    // once, and can be reused for multiple requests.
    try (SecurityCenterClient client = SecurityCenterClient.create()) {
      ListFindingsRequest request =
          ListFindingsRequest.newBuilder()
              // To list findings across all sources, use "-".
              .setParent(
                  String.format("organizations/%s/sources/%s/locations/%s", organizationId,
                      sourceId,
                      location))
              .build();

      for (ListFindingsResult result : client.listFindings(request).iterateAll()) {
        System.out.printf("Finding: %s", result.getFinding().getName());
      }
      System.out.println("\nListing complete.");
    }
  }
}

Node.js

// Imports the Google Cloud client library.
const {SecurityCenterClient} = require('@google-cloud/security-center').v2;

// Creates a new client.
const client = new SecurityCenterClient();

// TODO(developer): Update the following for your own environment.
const organizationId = '1081635000895';
const location = 'global';

// Required. Name of the source the findings belong to. If no location is
// specified, the default is global. The following list shows some examples:
// - `organizations/[organization_id]/sources/[source_id]/locations/[location_id]`
// - `folders/[folder_id]/sources/[source_id]`
// - `folders/[folder_id]/sources/[source_id]/locations/[location_id]`
// - `projects/[project_id]/sources/[source_id]`
// - `projects/[project_id]/sources/[source_id]/locations/[location_id]`
// To groupBy across all sources provide a source_id of `-`.
const parent = `organizations/${organizationId}/sources/-/locations/${location}`;

// Build the list findings request.
const listFindingsRequest = {
  parent,
};

async function listAllFindings() {
  // Call the API.
  const iterable = client.listFindingsAsync(listFindingsRequest);
  let count = 0;

  for await (const response of iterable) {
    // Just print a few for demonstration.
    if (count > 5) break;
    console.log(
      `${++count} ${response.finding.name} ${response.finding.resourceName}`
    );
  }
}

await listAllFindings();

Python

def list_all_findings(organization_id, source_name, location_id) -> int:
    """
    lists all findings for a source
    Args:
       organization_id: organization_id is the numeric ID of the organization. e.g.:organization_id = "111122222444"
       source_name: is the resource path for a source that has been created
       location_id: GCP location id; example: 'global'
    Returns:
        int: returns the count of all findings for a source
    """
    from google.cloud import securitycenter_v2

    # Create a client.
    client = securitycenter_v2.SecurityCenterClient()
    parent = f"organizations/{organization_id}"
    all_sources = f"{parent}/sources/{source_name}/locations/{location_id}"

    # Create the request dictionary
    request = {"parent": all_sources}

    # Print the request for debugging
    print("Request: ", request)

    finding_result_iterator = client.list_findings(request={"parent": all_sources})
    for count, finding_result in enumerate(finding_result_iterator):
        print(
            "{}: name: {} resource: {}".format(
                count, finding_result.finding.name, finding_result.finding.resource_name
            )
        )
    return finding_result_iterator

各検出結果の出力は、次のような形になります。

{
  "finding": {
    "name": "organizations/ORGANIZATION_ID/sources/SOURCE_ID/findings/FINDING_ID",
    "parent": "organizations/ORGANIZATION_ID/sources/SOURCE_ID",
    "resourceName": "//cloudresourcemanager.googleapis.com/projects/PROJECT_NUMBER",
    "state": "ACTIVE",
    "category": "Malware: Cryptomining Bad Domain",
    "sourceProperties": {
      "sourceId": {
        "projectNumber": "PROJECT_NUMBER",
        "customerOrganizationNumber": "ORGANIZATION_ID"
      },
      "detectionCategory": {
        "technique": "cryptomining",
        "indicator": "domain",
        "ruleName": "bad_domain",
        "subRuleName": "cryptomining"
      },
      "detectionPriority": "LOW",
      "affectedResources": [
        {
          "gcpResourceName": "//cloudresourcemanager.googleapis.com/projects/PROJECT_NUMBER"
        }
      ],
      "evidence": [
        {
          "sourceLogId": {
            "projectId": "PROJECT_ID",
            "resourceContainer": "projects/PROJECT_ID",
            "timestamp": {
              "seconds": "1636566099",
              "nanos": 5.41483849E8
            },
            "insertId": "INSERT_ID"
          }
        }
      ],
      "properties": {
        "domains": ["DOMAIN"],
        "instanceDetails": "/projects/PROJECT_ID/zones/ZONE/instances/INSTANCE_ID",
        "network": {
          "project": "PROJECT_ID",
          "location": "ZONE"
        },
        "dnsContexts": [
          {
            "authAnswer": true,
            "sourceIp": "SOURCE_IP_ADDRESS",
            "queryName": "DOMAIN",
            "queryType": "A",
            "responseCode": "NXDOMAIN"
          }
        ],
        "vpc": {
          "vpcName": "default"
        }
      },
      "findingId": "FINDING_ID",
      "contextUris": {
        "mitreUri": {
          "displayName": "MITRE Link",
          "url": "https://attack.mitre.org/techniques/T1496/"
        },
        "virustotalIndicatorQueryUri": [
          {
            "displayName": "VirusTotal Domain Link",
            "url": "https://www.virustotal.com/gui/domain/DOMAIN/detection"
          }
        ],
        "cloudLoggingQueryUri": [
          {
            "displayName": "Cloud Logging Query Link",
            "url": "https://console.cloud.google.com/logs/query;query\u003dtimestamp%3D%222021-11-10T17:41:39.541483849Z%22%0AinsertId%3D%22INSERT_ID%22%0Aresource.labels.project_id%3D%22PROJECT_ID%22?project\u003dPROJECT_ID"
          }
        ],
        "relatedFindingUri": {}
      }
    },
    "securityMarks": {
      "name": "organizations/ORGANIZATION_ID/sources/SOURCE_ID/findings/FINDING_ID/securityMarks"
    },
    "eventTime": "2021-11-10T17:41:41.594Z",
    "createTime": "2021-11-10T17:41:42.014Z",
    "severity": "LOW",
    "workflowState": "NEW",
    "canonicalName": "projects/PROJECT_NUMBER/sources/SOURCE_ID/findings/FINDING_ID",
    "mute": "UNDEFINED",
    "findingClass": "THREAT",
    "indicator": {
      "domains": ["DOMAIN"]
    }
  },
  "resource": {
    "name": "//cloudresourcemanager.googleapis.com/projects/PROJECT_NUMBER",
    "projectName": "//cloudresourcemanager.googleapis.com/projects/PROJECT_NUMBER",
    "projectDisplayName": "PROJECT_ID",
    "parentName": "//cloudresourcemanager.googleapis.com/organizations/ORGANIZATION_ID",
    "parentDisplayName": "PARENT_NAME",
    "type": "google.cloud.resourcemanager.Project",
    "displayName": "PROJECT_ID"
  }
}

検出結果をフィルタする

プロジェクト、フォルダ、組織には、検出結果が多数含まれている可能性があります。上記の例ではフィルタが使用されていないため、すべての検出レコードが返されます。

必要なフィールドの情報のみを取得するには、検出フィルタを使用します。これらのフィルタは、SQL ステートメントの「WHERE」句に似ていますが、列ではなく API によって返されるオブジェクトに適用されます。

次の例では、カテゴリ「MEDIUM_RISK_ONE」を含む検出結果のみを一覧表示します。カテゴリのセットは、検索プロバイダ(セキュリティ ソース)によって異なります。フィルタで使用できるカテゴリを確認するには、検出プロバイダのドキュメントをご覧ください。

gcloud

検出結果をフィルタするには、次のコマンドを使用します。

gcloud scc findings list PARENT_TYPE/PARENT_ID \
  --location=LOCATION \
  --source=SOURCE_ID \
  --filter="FILTER"

次のように置き換えます。

  • PARENT_TYPE: 検出結果を一覧表示するリソース階層のレベル。organizationsfoldersprojects のいずれかを使用します。
  • PARENT_ID: 組織、フォルダ、プロジェクトの数値 ID、または英数字のプロジェクト ID。
  • LOCATION: データ所在地が有効になっている場合は、フィルタを使用して検出結果を一覧表示する Security Command Center のロケーション。データ所在地が有効になっていない場合は、値 global を使用します。
  • SOURCE_ID: 検出結果のタイプを示すセキュリティ ソースの ID。
  • FILTER: 使用するフィルタ。たとえば、次のフィルタでは、MEDIUM_RISK_ONE のカテゴリの検出結果のみが返されます。
    --filter="category=\"MEDIUM_RISK_ONE\""

他の例については、次のコマンドを実行します。

gcloud scc findings list --help

ドキュメントの例については、gcloud scc findings list をご覧ください。

Go

import (
	"context"
	"fmt"
	"io"

	securitycenter "cloud.google.com/go/securitycenter/apiv2"
	"cloud.google.com/go/securitycenter/apiv2/securitycenterpb"
	"google.golang.org/api/iterator"
)

// listFilteredFindings prints findings with category 'MEDIUM_RISK_ONE' for a
// specific source to w. sourceName is the full resource name of the source
// to search for findings under.
func listFilteredFindings(w io.Writer, sourceName string) error {
	// Specific source:
	// 		sourceName := "{parent}/sources/{sourceId}"
	// All sources:
	// 		sourceName := "{parent}/sources/-"
	// where,
	// Parent must be in one of the following formats:
	//		"organizations/{orgId}"
	//		"projects/{projectId}"
	//		"folders/{folderId}"
	// Instantiate a context and a security service client to make API calls.
	ctx := context.Background()
	client, err := securitycenter.NewClient(ctx)
	if err != nil {
		return fmt.Errorf("securitycenter.NewClient: %w", err)
	}
	defer client.Close() // Closing the client safely cleans up background resources.

	req := &securitycenterpb.ListFindingsRequest{
		Parent: sourceName,
		Filter: `category="MEDIUM_RISK_ONE"`,
	}
	it := client.ListFindings(ctx, req)
	for {
		result, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return fmt.Errorf("it.Next: %w", err)
		}
		finding := result.Finding
		fmt.Fprintf(w, "Finding Name: %s, ", finding.Name)
		fmt.Fprintf(w, "Resource Name %s, ", finding.ResourceName)
		fmt.Fprintf(w, "Category: %s\n", finding.Category)
	}
	return nil
}

Java


import com.google.cloud.securitycenter.v2.ListFindingsRequest;
import com.google.cloud.securitycenter.v2.ListFindingsResponse.ListFindingsResult;
import com.google.cloud.securitycenter.v2.SecurityCenterClient;
import java.io.IOException;

public class ListFindingsWithFilter {

  public static void main(String[] args) throws IOException {
    // TODO: Replace the variables within {}
    // organizationId: Google Cloud Organization id.
    // You can also use project/ folder as the parent resource.
    String organizationId = "google-cloud-organization-id";

    // Specify the location to list the findings.
    String location = "global";

    // The source id to scope the findings.
    String sourceId = "source-id";

    listFilteredFindings(organizationId, sourceId, location);
  }

  // List filtered findings under a source.
  public static void listFilteredFindings(String organizationId, String sourceId, String location)
      throws IOException {
    // Initialize client that will be used to send requests. This client only needs to be created
    // once, and can be reused for multiple requests.
    try (SecurityCenterClient client = SecurityCenterClient.create()) {

      // Use any one of the following formats:
      //  * organizations/{organization_id}/sources/{source_id}/locations/{location}
      //  * folders/{folder_id}/sources/{source_id}/locations/{location}
      //  * projects/{project_id}/sources/{source_id}/locations/{location}
      String parent = String.format("organizations/%s/sources/%s/locations/%s", organizationId,
          sourceId,
          location);

      // Listing all findings of category "MEDIUM_RISK_ONE".
      String filter = "category=\"MEDIUM_RISK_ONE\"";

      ListFindingsRequest request =
          ListFindingsRequest.newBuilder()
              .setParent(parent)
              .setFilter(filter)
              .build();

      for (ListFindingsResult result : client.listFindings(request).iterateAll()) {
        System.out.printf("Finding: %s", result.getFinding().getName());
      }
      System.out.println("\nListing complete.");
    }
  }
}

Node.js

// Imports the Google Cloud client library.
const {SecurityCenterClient} = require('@google-cloud/security-center').v2;

// Creates a new client.
const client = new SecurityCenterClient();

// TODO(developer): Update the following for your own environment.
const organizationId = '1081635000895';
const location = 'global';

// Required. Name of the source to groupBy. If no location is specified,
// finding is assumed to be in global.
//  The following list shows some examples:
// - `organizations/[organization_id]/sources/[source_id]`
// - `organizations/[organization_id]/sources/[source_id]/locations/[location_id]`
// - `folders/[folder_id]/sources/[source_id]`
// - `folders/[folder_id]/sources/[source_id]/locations/[location_id]`
// - `projects/[project_id]/sources/[source_id]`
// - `projects/[project_id]/sources/[source_id]/locations/[location_id]`
// To groupBy across all sources provide a source_id of `-`.
const parent = `organizations/${organizationId}/sources/-/locations/${location}`;

// Listing all findings of category "MEDIUM_RISK_ONE".
const filter = 'category="MEDIUM_RISK_ONE"';

// Build the list findings with filter request.
const listFilteredFindingsRequest = {
  parent,
  filter,
};

async function listFilteredFindings() {
  // Call the API.
  const iterable = client.listFindingsAsync(listFilteredFindingsRequest);
  let count = 0;
  console.log('Findings:');
  for await (const response of iterable) {
    // Just print a few for demonstration.
    if (count > 5) break;
    console.log(
      `${++count} ${response.finding.name} ${response.finding.resourceName}`
    );
  }
}
await listFilteredFindings();

Python

def list_filtered_findings(organization_id, source_name, location_id) -> int:
    """
    lists filtered findings for a source
    Args:
        organization_id: organization_id is the numeric ID of the organization. e.g.:organization_id = "111122222444"
        source_name: is the resource path for a source that has been created
        location_id: GCP location id; example: 'global'
    Returns:
         int: returns the filtered findings for a source
    """
    count = 0
    from google.cloud import securitycenter_v2

    # Create a new client.
    client = securitycenter_v2.SecurityCenterClient()
    parent = f"organizations/{organization_id}"
    all_sources = f"{parent}/sources/{source_name}/locations/{location_id}"
    finding_result_iterator = client.list_findings(
        request={"parent": all_sources, "filter": 'severity="LOW"'}
    )
    # Iterate an print all finding names and the resource they are
    # in reference to.
    for count, finding_result in enumerate(finding_result_iterator):
        print(
            "{}: name: {} resource: {}".format(
                count, finding_result.finding.name, finding_result.finding.resource_name
            )
        )
    return count

Security Command Center では、使用される可能性があるプロパティ タイプとして、完全な JSON 配列とオブジェクトもサポートされています。以下に基づいてフィルタできます。

  • 配列要素
  • オブジェクト内で文字列が部分的に一致する完全な JSON オブジェクト
  • JSON オブジェクトのサブフィールド

サポートされている演算子

Security Command Center の検出結果のクエリ ステートメントは、ほとんどの Google Cloud APIs でサポートされている演算子をサポートしています。

次のリストでは、さまざまな演算子の使用を示します。

  • state="ACTIVE" AND NOT mute="MUTED"
  • create_time>"2023-08-15T19:05:32.428Z"
  • resource.parent_name:"prod"
  • severity="CRITICAL" OR severity="HIGH"

次のリストでは、検出結果のクエリ ステートメントでサポートされているすべての演算子と関数を示します。

  • 文字列:
    • =(完全一致)
    • :(部分一致)
  • 数字:
    • <><=>=(不等式)
    • =!=(等式)
  • ブール値:
    • =(等式)
  • 論理関係:
    • AND
    • OR
    • NOT または -
  • 式のグループ化:
    • ()(括弧)
  • 配列:
    • contains(): 指定したフィルタに一致する要素を 1 つ以上含む配列フィールドを使用して、検出結果をクエリする関数
    • containsOnly(): 指定したフィルタに一致する要素のみを含む配列フィールドを使用して、検出結果をクエリする関数
  • IP アドレス:
    • inIpRange(): 指定した CIDR 範囲内の IP アドレスをクエリする関数

IP アドレスでのフィルタ

一部の検出結果プロパティには IP アドレスが含まれます。検出結果は、特定の IP アドレスや IP アドレスの範囲に基づいてフィルタできます。

IP アドレスは、次のような検出結果と検出結果プロパティに文字列として表示されます。

  • access.caller_ip
  • connections.destinationIp
  • connections.sourceIp
  • indicator.ip_addresses

特定の IP アドレスをフィルタするには、次の例に示すように、等価演算子を使用します。

access.caller_ip="192.0.2.0"

IP アドレスの範囲に基づいて検出結果をフィルタするには、inIpRange 関数を使用します。inIpRange 関数を使用して、検出結果を、指定した CIDR 範囲内の IP アドレスを含む検出結果だけに絞り込みます。inIpRangeNOT 演算子を使用すると、検出結果を、指定した CIDR 範囲外の IP アドレスを含む検出結果のみにフィルタできます。

次の例では、inIpRange 関数の構文を示します。

inIpRange(IP_FINDING_FIELD, "CIDR_RANGE")

配列を含む検出結果フィールドの配列要素に IP アドレスがある場合は、次の構文で contains 関数と inIpRange 関数の両方を使用します。

contains(ATTRIBUTE_WITH_ARRAY, inIpRange(IP_FINDING_FIELD, "CIDR_RANGE"))

次の例の inIpRange 関数は、192.0.2.0/24 の CIDR 範囲にある IP アドレスについて、connections 検出結果フィールドに含まれる配列の各 destination_ip 要素を評価します。

contains(connections, inIpRange(destination_ip, "192.0.2.0/24"))

次の例では、inIpRange 関数を使用し、connections.source_ip フィールドの IP アドレスが 1 つの範囲内にあり、別の範囲内にはない検出結果をフィルタする gcloud CLI コマンドを示します。connections フィールドは配列型フィールドのため、contains 関数を使用します。

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --filter="contains(connections, inIpRange(source_ip, \"2001:db8::/32\")) \
      AND NOT contains(connections, inIpRange(source_ip, \"192.0.2.0/24\"))"

JSON オブジェクトの例

このページの例では、次の JSON オブジェクトが検出属性であることを前提としています。

{
  "outer_object": {
    "middle_object": {
      "deeply_nested_object": {
        "x": 123
      },
      "y": "some-string-value"
    },
    "list_middle_object": [
      {
        "v": 321,
        "w": [
          {
            "a": 3,
            "b": 4
          }
        ]
      }
    ],
    "z": "some-other-string-value",
    "u": [
      "list-element-1",
      "list-element-2",
      "list-element-3"
    ]
  }
}

検出結果のフィルタリングの例

前の JSON の例で、検出属性の名前が my_property であるとします。次の例には、オブジェクトをプロパティとして持つ検出結果に対するクエリが含まれています。また、クエリで ANDOR を使用して、他のフィルタと一緒にこれらのフィルタを使用することもできます。

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --filter="my_property.outer_object.middle_object.deeply_nested_object.x = 123"

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --filter="my_property.outer_object.middle_object.y = \"some-string-value\""

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --filter="my_property.outer_object.middle_object.y : \"string-value\""

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --filter="my_property.outer_object.z = \"some-other-string-value\""

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --filter="my_property.outer_object.z : \"other-string-value\""

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --filter="my_property.outer_object.u : \"list-element-1\""

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --filter="my_property.outer_object.u : \"list-element-2\""

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --filter="my_property.outer_object.u : \"list-element-3\""

配列型フィールドのサブフィルタ

ListFindings を呼び出す際に部分文字列一致 : を使用できます。この照合では、配列のコンテンツ全体に対して部分的に一致する文字列が 1 回チェックされます。または、次のいずれかの関数を使用して、配列とそのサブフィールドの要素に対してサブフィルタを直接実行することもできます。

  • contains() 関数は、配列の要素に指定された値が含まれている場合に検出結果を返します。

  • containsOnly() 関数。配列のすべての要素がサブフィルタと一致する場合にのみ、検出結果を返します。

これらの関数は、次のようなサブフィルタ クエリ機能をサポートしています。

  • 完全一致の要素: 完全に一致する文字列 "example" を含む配列要素を照合します。
  • 特定の数値演算: 100 以上の配列要素を照合します。
  • 配列構造に対する複雑なフィルタリング: プロパティ x を含む配列要素を、対応する値 y と照合します。

contains() 関数の形式

contains() 関数の形式は次のとおりです。

contains(ARRAY_ATTRIBUTE_NAME, SUBFILTER)

次のように置き換えます。

  • ARRAY_ATTRIBUTE_NAME: 配列(リスト)型のフィールドまたはサブフィールド。
  • SUBFILTER: 配列内で検索する値を定義する式。サブフィルタの形式は、ARRAY_ATTRIBUTE_NAMEオブジェクトの配列プリミティブ型要素の配列かによって異なります。ARRAY_ATTRIBUTE_NAME が、ネストされている配列を含むオブジェクトの配列の場合は、範囲指定されたサブフィルタを使用して、同じ ARRAY_ATTRIBUTE_NAME 要素内ですべての条件を満たすものを取得するように指定できます。

Security Command Center API は、ARRAY_ATTRIBUTE_NAMESUBFILTER を満たす要素が少なくとも 1 つ含まれている検出結果を返します。

containsOnly() 関数の形式

containsOnly() 関数の形式は次のとおりです。

containsOnly(ARRAY_ATTRIBUTE_NAME, SUBFILTER)

次のように置き換えます。

  • ARRAY_ATTRIBUTE_NAME: 配列(リスト)型のフィールドまたはサブフィールド。Security Command Center API を使用してクエリを実行する場合は、使用可能な配列属性に containsOnly() 関数を使用できます。
  • SUBFILTER: 配列内で検索する値を定義する式。サブフィルタの形式は、ARRAY_ATTRIBUTE_NAMEオブジェクトの配列プリミティブ型要素の配列かによって異なります。ARRAY_ATTRIBUTE_NAME が、ネストされている配列を含むオブジェクトの配列の場合は、範囲指定されたサブフィルタを使用して、同じ ARRAY_ATTRIBUTE_NAME 要素内ですべての条件を満たすものを取得するように指定できます。

Security Command Center API は、すべての ARRAY_ATTRIBUTE_NAME 要素が SUBFILTER と一致する検出結果を返します。

オブジェクトの配列のサブフィルタ

以下に、前述の JSON の例の抜粋を示します。ここで、list_middle_object フィールドはオブジェクトの配列です。

    "list_middle_object": [
      {
        "v": 321,
        "w": [
          {
            "a": 3,
            "b": 4
          }
        ]
      }
    ]

次の例では、list_middle_object フィールドの 1 つ以上の要素に v サブフィールドがあり、その値が 321 以上であるものをクエリしています。

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --filter="contains(my_property.outer_object.list_middle_object, v  >= 321)"

contains() 関数と containsOnly() 関数を使用する具体的な例については、特定の配列値を含む検出結果をご覧ください。

プリミティブ型要素を含む配列のサブフィルタ

プリミティブ型は文字列、数値、ブール値です。プリミティブ型を含む配列に対して contains() 関数を使用するには、特別なキーワード elem を使用します。

以下に、前述の JSON の例の抜粋を示します。ここで、u フィールドはプリミティブ型要素の配列です。

"u": ["list-element-1", "list-element-2", "list-element-3"]

次の例では、u フィールドの 1 つ以上の要素が list-element-1 である検出結果をクエリしています。

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --filter="contains(my_property.outer_object.u, elem = \"list-element-1\")"

contains() 関数を使用する実際の例については、特定の配列値を含む検出結果をご覧ください。

範囲指定されたサブフィルタ

以下に、前述の JSON の例の抜粋を示します。ここで、list_middle_object フィールドはオブジェクトの配列で、この配列のオブジェクトにネストされた配列が含まれています。

"list_middle_object": [
  {
    "v": 321,
    "w": [
      {
        "a": 3,
        "b": 4
      }
    ]
  }
]

次の例では、同じ list_middle_object 要素内で次の条件をすべて満たしている検出結果をクエリしています。

  • v サブフィールドの値が 321 以上です。
  • w サブフィールドに、a プロパティが 3 の要素が含まれていない。
gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --filter="contains(my_property.outer_object.list_middle_object, v  >= 321 AND -contains(w, a = 3))"

contains() 関数を使用する実際の例については、特定の配列値を含む検出結果をご覧ください。

検出結果の並べ替えの例

検出結果は、文字列、数値、ブール値などのプリミティブ型の厳密なサブフィールドで並べ替えることができます。前の JSON の例で、検出属性の名前が my_property であるとします。次の例では、検出結果フィールドを並べ替えるクエリを示します。キーワード DESC は、対象のフィールドを降順にすることを示します。デフォルトの順序は昇順です。

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --order-by="my_property.outer_object.middle_object.deeply_nested_object.x DESC"

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --order-by="my_property.outer_object.middle_object.deeply_nested_object.x"

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --order-by="my_property.outer_object.middle_object.y DESC"

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --order-by="my_property.outer_object.middle_object.y"

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --order-by="my_property.outer_object.z DESC"

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --order-by="my_property.outer_object.z"

フィルタの例

次のセクションでは、検索フィルタの実用的な例を紹介します。

特定の時点より後の検出結果のフィルタ

次に示すサンプル フィルタは、2019 年 6 月 5 日水曜日の、午後 10 時 12 分 5 秒(GMT)以降に発生した検出結果と一致します。event_time フィルタを使用すると、次の形式と型を使用して時間を表すことができます。

  • 整数リテラルとしての Unix エポック時間(ミリ秒単位)

    "event_time > 1559772725000"
    
  • 文字列リテラルとしての RFC 3339

    "event_time > \"2019-06-05T22:34:40+00:00\""
    

配列型フィールドのフィルタ

次の例は、フィルタ内の配列型フィールドでの文字列部分一致を使用する方法を示しています。

"indicator.domains : \"website.com\""

Security Command Center API は、配列内の文字列の一部に website.com が含まれている検出結果を返します。たとえば、「website.com」は配列内の要素の部分文字列であるため、indicator.domains = [\"onewebsite.com\"] の検出結果と一致します。

次のセクションのフィルタの例では、contains() 関数を使用した配列型フィルタを使用するためのオプションについて説明します。

vulnerability.cve.references フィールドのフィルタ

次の例では、vulnerability.cve.references 配列の少なくとも 1 つの要素に、SOURCE_OF_REFERENCE と等しい source プロパティと FILTERED_URI を持つ uri プロパティの両方が含まれています。

"contains(vulnerability.cve.references, source = \"SOURCE_OF_REFERENCE\" AND uri : \"FILTERED_URI\")"

次のように置き換えます。

indicator.domains フィールドのフィルタ

次の例では、少なくとも 1 つの指標ドメインに mycompanyprefix.ca の両方が含まれる検出結果が返されます。

"contains(indicator.domains, elem : \"mycompanyprefix\" AND elem : \".ca\")"

indicator.ip_addresses フィールドのフィルタ

次の例では、indicator.ip_addresses 配列内の少なくとも 1 つの要素が IP_ADDRESS と等しい検出結果が返されます。

"contains(indicator.ip_addresses, elem = \"IP_ADDRESS\")"

IP_ADDRESS は、検索する検出結果に関連付けられた IP アドレスに置き換えます。

外部システムの割り当て先のフィルタ

次の例では、external_systems.EXTERNAL_SYSTEM_NAME.assignees 配列内の少なくとも 1 つの要素が ASSIGNEE と等しい検出結果が返されます。

"contains(external_systems.EXTERNAL_SYSTEM_NAME.assignees, elem = \"ASSIGNEE\")"

次のように置き換えます。

  • EXTERNAL_SYSTEM_NAME: サードパーティの SIEM/SOAR システムの名前(例: demisto)。
  • ASSIGNEE: 外部システムの割り当て先。

resource.folders.resource_folder フィールドのフィルタ

次の例では、resource.folders.resource_folder 配列内の少なくとも 1 つの要素が FOLDER_NAME と等しくない検出結果が返されます。

"contains(resource.folders.resource_folder, -(elem = \"FOLDER_NAME\"))"

resource.folders.resource_folder_display_name フィールドのフィルタ

次の例では、resource.folders.resource_folder_display_name 配列内の少なくとも 1 つの要素が DISPLAY_NAME と等しい検出結果が返されます。

"contains(resource.folders.resource_folder_display_name, elem = \"DISPLAY_NAME\")"

DISPLAY_NAME は、検索する検出結果に関連付けられたフォルダのユーザー定義の名前に置き換えます。

特定のサービス アカウントのみを含むフィルタ

次の例では、すべての iam_bindings エントリのメンバー値が指定されたサービス アカウントのいずれかと等しい場合にのみ、検出結果が返されます。

containsOnly(iam_bindings, (member = SERVICE_ACCOUNT1 OR member = SERVICE_ACCOUNT2 OR member = "SERVICE_ACCOUNT3 "))

SERVICE_ACCOUNT1SERVICE_ACCOUNT2SERVICE_ACCOUNT3 は、サービス アカウントのメールアドレスに置き換えます。

検出結果フィルタで contains() 関数と containsOnly() 関数を使用する方法については、配列型フィールドのサブフィルタをご覧ください。

次のステップ

検出通知の設定の詳細を学習する。