使用 Security Command Center API 列出安全性发现结果

Security Command Center 发现结果模型可为项目或组织中资源的潜在安全风险建模。发现结果始终与 Security Command Center 中的特定资源相关。

本指南介绍了如何使用 Security Command Center 客户端库访问发现结果。每个结果都属于一个来源。大多数检测器或发现结果提供程序在同一来源内生成发现结果。

Security Command Center 的 IAM 角色可以在组织、文件夹或项目级层授予。您能否查看、修改、创建或更新发现结果、资产和安全来源,取决于您获授予的访问权限级别。如需详细了解 Security Command Center 角色,请参阅访问权限控制

准备工作

在设置来源之前,您需要完成以下操作:

页面大小

所有 Security Command Center list API 都是分页的。每个响应都会返回一个结果页面和一个返回下一页的令牌。页面大小可配置。默认页面大小为 10。您可以将其设置为介于最小值 1 和最大值 1000 之间的值。

发现结果保留

您可以在至少 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 API 支持的运算符。

以下列表展示了各种运算符的用法:

  • 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():一个函数,用于查询包含至少一个与指定过滤条件匹配的元素的数组字段中的发现结果
    • 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 地址的发现结果。通过将 NOT 运算与 inIpRange 结合使用,您可以将发现结果过滤为仅显示包含指定 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"))

以下示例展示了一个 gcloud CLI 命令,该命令使用 inIpRange 函数来过滤 connections.source_ip 字段中属于一个范围但不属于另一个范围的 IP 地址。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 时,您可以使用子字符串匹配 :,它会对整个数组内容的部分字符串匹配进行一次检查。或者,您也可以使用以下任一函数直接针对数组及其子字段的元素运行子过滤条件:

  • 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_NAME 至少包含一个满足 SUBFILTER 的元素。

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 字段中至少一个元素具有值大于或等于 321 的 v 子字段:

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 字段中至少一个元素是“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:05 (格林尼治标准时间)之后的最新发现结果。借助 event_time 过滤器,您可以使用以下格式和类型表示时间:

  • 以整数字面量表示的 Unix Epoch 时间(以毫秒为单位)

    "event_time > 1559772725000"
    
  • 作为字符串字面量的 RFC 3339

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

按数组类型字段进行过滤

以下示例显示了在过滤条件中对数组类型字段使用部分字符串匹配:

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

Security Command Center API 会返回数组中包含 website.com 部分字符串的任一发现结果。例如,它匹配具有 indicator.domains = [\"onewebsite.com\"] 的发现结果,因为“website.com”是数组元素中的子字符串。

在以下部分中,示例过滤条件展示了使用 contains() 函数进行富数组类型过滤的一些选项。

vulnerability.cve.references 字段进行过滤

以下示例返回一些发现结果,其中 vulnerability.cve.references 数组中至少一个元素具有 source 属性(等于 SOURCE_OF_REFERENCE)和 uri 属性(含有 FILTERED_URI)。

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

替换以下内容:

  • SOURCE_OF_REFERENCE常见漏洞和披露 (CVE) 参考的来源的名称,例如 NVD
  • FILTERED_URI:CVE 参考来源的 URI。

indicator.domains 字段进行过滤

以下示例返回一些发现结果,其中至少一个指标网域同时具有 mycompanyprefix.ca

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

indicator.ip_addresses 字段进行过滤

以下示例返回一些发现结果,其中 indicator.ip_addresses 数组中至少一个元素等于 IP_ADDRESS

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

IP_ADDRESS 替换为与要搜索的发现结果关联的 IP 地址。

按外部系统分配对象进行过滤

以下示例返回一些发现结果,其中 external_systems.EXTERNAL_SYSTEM_NAME.assignees 数组中至少一个元素等于 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 数组中至少一个元素不等于 FOLDER_NAME

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

resource.folders.resource_folder_display_name 字段进行过滤

以下示例返回一些发现结果,其中 resource.folders.resource_folder_display_name 数组中至少一个元素等于 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() 函数,请参阅数组类型字段的子过滤条件

后续步骤

详细了解如何设置发现结果通知