使用 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_ID

PARENT_ID 替换为以下某个值:

  • 组织 ID,格式如下:ORGANIZATION_ID(仅限数字 ID)
  • 文件夹 ID,格式如下:folders/FOLDER_ID
  • 项目 ID,格式如下:projects/PROJECT_ID

如需查看更多示例,请运行以下命令:

gcloud scc findings list --help

如需查看文档中的示例,请参阅 gcloud scc 发现结果列表

Python

from google.cloud import securitycenter

# Create a client.
client = securitycenter.SecurityCenterClient()

# 'parent' must be in one of the following formats:
#   "organizations/{organization_id}"
#   "projects/{project_id}"
#   "folders/{folder_id}"
parent = f"organizations/{organization_id}"
# The "sources/-" suffix lists findings across all sources.  You
# also use a specific source_name instead.
all_sources = f"{parent}/sources/-"
finding_result_iterator = client.list_findings(request={"parent": all_sources})
for i, finding_result in enumerate(finding_result_iterator):
    print(
        "{}: name: {} resource: {}".format(
            i, finding_result.finding.name, finding_result.finding.resource_name
        )
    )

Java

static ImmutableList<ListFindingsResult> listAllFindings(OrganizationName organizationName) {
  try (SecurityCenterClient client = SecurityCenterClient.create()) {
    // Input parameters for SourceName must be in one of the following formats:
    //    * OrganizationName organizationName = OrganizationName.of("organization-id");
    //      organizationName.getOrganization();
    //    * ProjectName projectName = ProjectName.of("project-id");
    //      projectName.getProject();
    //    * FolderName folderName = FolderName.of("folder-id");
    //      folderName.getFolder();
    //
    // "-" Indicates listing across all sources.
    SourceName sourceName = SourceName.of(organizationName.getOrganization(), "-");

    ListFindingsRequest.Builder request =
        ListFindingsRequest.newBuilder().setParent(sourceName.toString());

    // Call the API.
    ListFindingsPagedResponse response = client.listFindings(request.build());

    // This creates one list for all findings.  If your organization has a large number of
    // findings this can cause out of memory issues.  You can process them in incrementally
    // by returning the Iterable returned response.iterateAll() directly.
    ImmutableList<ListFindingsResult> results = ImmutableList.copyOf(response.iterateAll());
    System.out.println("Findings:");
    System.out.println(results);
    return results;
  } catch (IOException e) {
    throw new RuntimeException("Couldn't create client.", e);
  }
}

Go

import (
	"context"
	"fmt"
	"io"

	securitycenter "cloud.google.com/go/securitycenter/apiv1"
	"cloud.google.com/go/securitycenter/apiv1/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/-"
		//		"projects/{projectId}/sources/-"
		//		"folders/{folderId}/sources/-"
		Parent: fmt.Sprintf("organizations/%s/sources/-", 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
}

Node.js

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

// Creates a new client.
const client = new SecurityCenterClient();
//  organizationId is the numeric ID of the organization.
/*
 * TODO(developer): Uncomment the following lines
 */
// const organizationId = "1234567777";

async function listAllFindings() {
  const [response] = await client.listFindings({
    // List findings across all sources.
    // parent: must be in one of the following formats:
    //    `organizations/${organization_id}/sources/-`
    //    `projects/${project_id}/sources/-`
    //    `folders/${folder_id}/sources/-`
    parent: `organizations/${organizationId}/sources/-`,
  });
  let count = 0;
  Array.from(response).forEach(result =>
    console.log(
      `${++count} ${result.finding.name} ${result.finding.resourceName}`
    )
  );
}
await listAllFindings();

每个发现结果的输出如下所示:

"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"
  }

过滤发现结果

一个项目、文件夹或组织可能会有许多发现结果。上面的示例未使用过滤条件,因此会返回所有发现结果记录。您可以通过 Security Command Center 使用发现结果过滤条件来获取所需发现结果的相关信息。

发现结果的过滤条件与 SQL 语句中的“where”子句类似,但列除外,列会应用于 API 返回的对象。

以下示例仅列出类别为“MEDIUM_RISK_ONE”的发现结果。不同的发现结果提供商(也称为安全来源)使用不同的类别集。如需确定可在过滤条件中使用的类别,请参阅发现结果提供商的文档。

gcloud

使用以下命令过滤发现结果:

gcloud scc findings list PARENT_ID --source=SOURCE_ID --filter="FILTER"

替换以下内容:

  • FILTER 替换为您需要使用的过滤条件。例如,以下过滤条件仅返回 MEDIUM_RISK_ONE 类别的发现结果:
    --filter="category=\"MEDIUM_RISK_ONE\""
  • PARENT_ID 替换为以下某个值:
    • 组织 ID,格式如下:ORGANIZATION_ID(仅限数字 ID)
    • 项目 ID,格式如下:projects/PROJECT_ID
    • 文件夹 ID,格式如下:folders/FOLDER_ID
  • SOURCE_ID 替换为提供发现结果类型的安全来源的 ID。

如需查看更多示例,请运行以下命令:

gcloud scc findings list --help

如需查看文档中的示例,请参阅 gcloud scc 发现结果列表

Python

from google.cloud import securitycenter

# Create a new client.
client = securitycenter.SecurityCenterClient()

# 'source_name' is the resource path for a source that has been
# created previously (you can use list_sources to find a specific one).
# Its format is:
# source_name = f"{parent}/sources/{source_id}"
# 'parent' must be in one of the following formats:
#   "organizations/{organization_id}"
#   "projects/{project_id}"
#   "folders/{folder_id}"
# You an also use a wild-card "-" for all sources:
#   source_name = "organizations/111122222444/sources/-"
finding_result_iterator = client.list_findings(
    request={"parent": source_name, "filter": 'category="MEDIUM_RISK_ONE"'}
)
# Iterate an print all finding names and the resource they are
# in reference to.
for i, finding_result in enumerate(finding_result_iterator):
    print(
        "{}: name: {} resource: {}".format(
            i, finding_result.finding.name, finding_result.finding.resource_name
        )
    )

Java

static ImmutableList<ListFindingsResult> listFilteredFindings(SourceName sourceName) {
  try (SecurityCenterClient client = SecurityCenterClient.create()) {
    // parentId: must be one of the following:
    //    "organization-id"
    //    "project-id"
    //    "folder-id"
    // SourceName sourceName = SourceName.of(parentId, sourceId);

    // Create filter to category of MEDIUM_RISK_ONE
    String filter = "category=\"MEDIUM_RISK_ONE\"";

    ListFindingsRequest.Builder request =
        ListFindingsRequest.newBuilder().setParent(sourceName.toString()).setFilter(filter);

    // Call the API.
    ListFindingsPagedResponse response = client.listFindings(request.build());

    // This creates one list for all findings.  If your organization has a large number of
    // findings this can cause out of memory issues.  You can process them in incrementally
    // by returning the Iterable returned response.iterateAll() directly.
    ImmutableList<ListFindingsResult> results = ImmutableList.copyOf(response.iterateAll());
    System.out.println("Findings:");
    System.out.println(results);
    return results;
  } catch (IOException e) {
    throw new RuntimeException("Couldn't create client.", e);
  }
}

Go

import (
	"context"
	"fmt"
	"io"

	securitycenter "cloud.google.com/go/securitycenter/apiv1"
	"cloud.google.com/go/securitycenter/apiv1/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
}

Node.js

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

// Creates a new client.
const client = new SecurityCenterClient();
//  sourceName is the full resource path of the source to search for
//  findings.
/*
 * TODO(developer): Uncomment the following lines
 */
// const sourceName = `${parent}/sources/${sourceId}`;
// where,
// parent: must be in one of the following formats:
//    `organizations/${organization_id}`
//    `projects/${project_id}`
//    `folders/${folder_id}`
async function listFilteredFindings() {
  const [response] = await client.listFindings({
    // List findings across all sources.
    parent: sourceName,
    filter: 'category="MEDIUM_RISK_ONE"',
  });
  let count = 0;
  Array.from(response).forEach(result =>
    console.log(
      `${++count} ${result.finding.name} ${result.finding.resourceName}`
    )
  );
}
listFilteredFindings();

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 example-organization.com \
    --source=123456789012345678 \
    --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 将这些过滤条件与其他过滤条件一起使用。

# ORGANIZATION_ID=organization-id
# SOURCE_ID="source-id"

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --filter="my_property.outer_object.middle_object.deeply_nested_object.x = 123"

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --filter="my_property.outer_object.middle_object.y = \"some-string-value\""

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --filter="my_property.outer_object.middle_object.y : \"string-value\""

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --filter="my_property.outer_object.z = \"some-other-string-value\""

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --filter="my_property.outer_object.z : \"other-string-value\""

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --filter="my_property.outer_object.u : \"list-element-1\""

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --filter="my_property.outer_object.u : \"list-element-2\""

gcloud scc findings list $ORGANIZATION_ID --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 子字段:

# ORGANIZATION_ID=organization-id
# SOURCE_ID="source-id"

gcloud scc findings list $ORGANIZATION_ID --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”:

# ORGANIZATION_ID=organization-id
# SOURCE_ID="source-id"

gcloud scc findings list $ORGANIZATION_ID --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 的元素。
# ORGANIZATION_ID=organization-id
# SOURCE_ID="source-id"

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --filter="contains(my_property.outer_object.list_middle_object, v  >= 321 AND -contains(w, a = 3))"

如需查看使用 contains() 函数的实际示例,请参阅包含特定数组值的发现结果

对发现结果进行排序的示例

您可以按原始类型(字符串,数字和布尔值)的严格子字段对结果进行排序。假设上一个 JSON 示例是名为 my_property 的发现结果属性。以下示例包含用于对发现结果字段进行排序的查询。关键字 DESC 指定其后面的字段必须按降序排序。默认顺序是升序。

# ORGANIZATION_ID=organization-id
# SOURCE_ID="source-id"

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --order-by="my_property.outer_object.middle_object.deeply_nested_object.x DESC"

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --order-by="my_property.outer_object.middle_object.deeply_nested_object.x"

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --order-by="my_property.outer_object.middle_object.y DESC"

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --order-by="my_property.outer_object.middle_object.y"

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --order-by="my_property.outer_object.z DESC"

gcloud scc findings list $ORGANIZATION_ID --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_URISOURCE_OF_REFERENCE 的 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_ACCOUNT2 和 将 SERVICE_ACCOUNT3 替换为服务账号的电子邮件地址。

了解如何在发现结果中使用 contains()containsOnly() 函数 请参阅对数组类型字段进行过滤

后续步骤

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