使用 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}`
    )
  );
}
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_ACCOUNT2SERVICE_ACCOUNT3 替换为服务帐号的电子邮件地址。

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

后续步骤

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