Listing security findings using the Security Command Center API

Security Command Center findings model the potential security risks of an organization's assets. A finding always relates to a specific asset in Security Command Center.

This guide shows you how to use Security Command Center client libraries to access an organization's findings. Each finding belongs to a source. Most detectors or finding providers produce findings within the same source.

Security Command Center roles are granted at the organization, folder, or project level. Your ability to view, edit, create, or update findings, assets, and security sources depends on the level for which you are granted access. To learn more about Security Command Center roles, see Access control.

Before you begin

Before you set up a source, you need to complete the following:

Page size

All Security Command Center list APIs are paginated. Each response returns a page of results and a token to return the next page. The page size is configurable. The default page size is 10. You can set it to a minimum of 1, and a maximum of 1000.

Findings retention

Findings contain a series of event_time snapshots that capture the state and properties of the finding each time the associated vulnerability or threat is encountered during scans.

Security Command Center stores finding snapshots for 13 months from their event_time, or the time the event took place. After 13 months, finding snapshots and their data are deleted from the Security Command Center database and cannot be recovered. This results in fewer snapshots in a finding, limiting the ability to view the history of a finding and how it's changed over time.

A finding persists in Security Command Center as long as it contains at least one snapshot with an event_time more recent than 13 months. To keep findings and all of their data for longer periods, export them to another storage location. To learn more, see Exporting Security Command Center data.

Listing all findings in an organization

gcloud

  # ORGANIZATION_ID=organization-id

  gcloud scc findings list $ORGANIZATION_ID

For more examples, run:

  gcloud scc findings list --help

Python

from google.cloud import securitycenter

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

# organization_id is the numeric ID of the organization. e.g.:
# organization_id = "111122222444"
org_name = "organizations/{org_id}".format(org_id=organization_id)
# The "sources/-" suffix lists findings across all sources.  You
# also use a specific source_name instead.
all_sources = "{org_name}/sources/-".format(org_name=org_name)
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()) {
    // OrganizationName organizationName = OrganizationName.of(/*organizationId=*/"123234324");
    // "-" 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"
	"google.golang.org/api/iterator"
	securitycenterpb "google.golang.org/genproto/googleapis/cloud/securitycenter/v1"
)

// 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: %v", err)
	}
	defer client.Close() // Closing the client safely cleans up background resources.

	req := &securitycenterpb.ListFindingsRequest{
		// List findings across all 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: %v", 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: `organizations/${organizationId}/sources/-`,
  });
  let count = 0;
  Array.from(response).forEach(result =>
    console.log(
      `${++count} ${result.finding.name} ${result.finding.resourceName}`
    )
  );
}
listAllFindings();

The output for each finding resembles the following:

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

Filtering findings

An organization might have many findings. The preceding example doesn't use a filter, so all finding records are returned. Security Command Center enables you to use finding filters to get information about only the findings you want.

Finding filters are like "where" clauses in SQL statements, but instead of columns, they apply to the objects returned by the API.

The following example lists only the findings that have a category "MEDIUM_RISK_ONE". Different finding providers (also known as security sources) use different sets of categories. To determine the categories you can use in your filter, refer to the documentation of the finding provider.

gcloud

  # ORGANIZATION_ID=organization-id
  # SOURCE_ID="source-id"
  FILTER="category=\"MEDIUM_RISK_ONE\""

  gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID --filter="$FILTER"

For more examples, run:

  gcloud scc findings list --help

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 = "organizations/{organization_id}/sources/{source_id}"
# e.g.:
# source_name = "organizations/111122222444/sources/1234"
# 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()) {
    // SourceName sourceName = SourceName.of(/*organizationId=*/"123234324",
    // /*sourceId=*/"423432321");

    // 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"
	"google.golang.org/api/iterator"
	securitycenterpb "google.golang.org/genproto/googleapis/cloud/securitycenter/v1"
)

// 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 := "organizations/111122222444/sources/1234"
	// All sources.
	// sourceName := "organizations/111122222444/sources/-"
	// 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: %v", 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: %v", 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 = "organizations/111122222444/sources/1234";

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 also supports full JSON arrays and objects as potential property types. You can filter on:

  • Array elements
  • Full JSON objects with partial string match within the object
  • JSON object subfields

Supported operators

Finding filters support common operators, including the following:

  • Strings:
    • Full equality =
    • Partial string matching :
  • Numbers:
    • Inequalities <, >, <=, >=
    • Equality =
  • Booleans:
    • Equality =
  • Logical operators:
    • AND
    • OR
    • Negation -

Example JSON object

The examples later on this page assume the following JSON object is a finding attribute:

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

Filtering findings example

Suppose the previous JSON example is a finding attribute named my_property. The following example includes queries for findings that have the object as a property. You can also use these filters with other filters using AND and OR in your query.

# 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\""

Filtering on array-type fields

When calling ListFindings, you can use a substring match :, which does a single check for a partial string match across the entire contents of the array. Alternatively, you can run a subfilter directly against elements of the array and its subfields using the contains() function. The contains() function offers an enhanced querying experience, which includes the following:

  • Exact element matching: Match array elements that contain the exact string, "example".
  • Specific number operations: Match array elements that are greater than or equal to 100.
  • Complex filtering against array structures: Match array elements that contain property x with a corresponding value y.

Format of the contains() function

The contains() function has the following format:

contains(ARRAY_FIELD, SUBFILTER)

Replace the following:

  • ARRAY_FIELD: a field or subfield that is of type array (a list).
  • SUBFILTER: an expression that defines the values to look for in the array. The subfilter's format differs depending on whether ARRAY_FIELD is an array of objects or an array of primitive-type elements. If the ARRAY_FIELD is an array of objects that have nested arrays, you can use a scoped subfilter to specify that you want all conditions to be satisfied within the same ARRAY_FIELD element.

The Security Command Center API returns findings where the ARRAY_FIELD contains at least one element that satisfies the SUBFILTER.

Subfilter for an array of objects

The following is an excerpt of the previous JSON example. Here, the list_middle_object field is an array of objects:

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

The following example queries for findings where at least one of the elements in the list_middle_object field has a v subfield with a value greater than or equal to 321:

# 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)"

For practical examples that use the contains() function, see Findings that contain specific array values.

Subfilter for an array that contains primitive-type elements

Primitive types are strings, numbers, and booleans. To use the contains() function against an array that contains primitive types, you use the special keyword, elem.

The following is an excerpt of the previous JSON example. Here, the u field is an array of primitive-type elements:

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

The following example queries for findings where at least one of the elements in the u field is "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\")"

For practical examples that use the contains() function, see Findings that contain specific array values.

Scoped subfilter

The following is an excerpt of the previous JSON example. Here, the list_middle_object field is an array of objects, and objects in this array contain a nested array.

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

The following example queries for findings where both of the following conditions are satisfied within the same list_middle_object element:

  • The v subfield has a value greater than or equal to 321.
  • The w subfield doesn't contain an element with an a property equal to 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))"

For practical examples that use the contains() function, see Findings that contain specific array values.

Sorting findings example

You can sort findings by strict subfields that are primitive types—strings, numbers, and booleans. Suppose the previous JSON example is a finding attribute named my_property. The following example includes queries to sort the finding fields. The keyword DESC specifies that the field it follows must be sorted in descending order. The default order is ascending.

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

Point-in-time queries

Security Command Center lets you list findings as of a particular snapshot time, up to 13 months old. For more information, see findings retention.

gcloud

  # ORGANIZATION_ID=organization-id
  # SOURCE_ID="source-id"
  # READ_TIME follows the format YYYY-MM-DDThh:mm:ss.ffffffZ
  READ_TIME=2019-02-28T07:00:06.861Z

  gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID --read-time=$READ_TIME

For more examples, run:

  gcloud scc findings list --help

Python

from datetime import datetime, timedelta

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 = "organizations/{organization_id}/sources/{source_id}"
# e.g.:
# source_name = "organizations/111122222444/sources/1234"
# You an also use a wild-card "-" for all sources:
#   source_name = "organizations/111122222444/sources/-"
five_days_ago = str(datetime.now() - timedelta(days=5))

finding_result_iterator = client.list_findings(
    request={"parent": source_name, "filter": five_days_ago}
)
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> listFindingsAtTime(SourceName sourceName) {
  try (SecurityCenterClient client = SecurityCenterClient.create()) {
    // SourceName sourceName = SourceName.of(/*organizationId=*/"123234324",
    // /*sourceId=*/"423432321");

    // 5 days ago
    Instant fiveDaysAgo = Instant.now().minus(Duration.ofDays(5));

    ListFindingsRequest.Builder request =
        ListFindingsRequest.newBuilder()
            .setParent(sourceName.toString())
            .setReadTime(
                Timestamp.newBuilder()
                    .setSeconds(fiveDaysAgo.getEpochSecond())
                    .setNanos(fiveDaysAgo.getNano()));

    // 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"
	"time"

	securitycenter "cloud.google.com/go/securitycenter/apiv1"
	"github.com/golang/protobuf/ptypes"
	"google.golang.org/api/iterator"
	securitycenterpb "google.golang.org/genproto/googleapis/cloud/securitycenter/v1"
)

// listFindingsAtTime prints findings that where present for a specific source
// as of five days ago to w. sourceName is the full resource name of the
// source to search for findings under.
func listFindingsAtTime(w io.Writer, sourceName string) error {
	// Specific source.
	// sourceName := "organizations/111122222444/sources/1234"
	// All sources.
	// sourceName := "organizations/111122222444/sources/-"
	ctx := context.Background()
	client, err := securitycenter.NewClient(ctx)
	if err != nil {
		return fmt.Errorf("securitycenter.NewClient: %v", err)
	}
	defer client.Close() // Closing the client safely cleans up background resources.
	fiveDaysAgo, err := ptypes.TimestampProto(time.Now().AddDate(0, 0, -5))
	if err != nil {
		return fmt.Errorf("Error converting five days ago: %v", err)
	}

	req := &securitycenterpb.ListFindingsRequest{
		Parent:   sourceName,
		ReadTime: fiveDaysAgo,
	}
	it := client.ListFindings(ctx, req)
	for {
		result, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return fmt.Errorf("it.Next: %v", 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 fully qualified source name to search for findings
// under.
/*
 * TODO(developer): Uncomment the following lines
 */
// const sourceName = "organizations/111122222444/sources/1234";

const fiveDaysAgo = new Date();
fiveDaysAgo.setDate(fiveDaysAgo.getDate() - 5);

async function listFindingsAtTime() {
  const [response] = await client.listFindings({
    // List findings across all sources.
    parent: sourceName,
    readTime: {
      seconds: Math.floor(fiveDaysAgo.getTime() / 1000),
      nanos: (fiveDaysAgo.getTime() % 1000) * 1e6,
    },
  });
  let count = 0;
  Array.from(response).forEach(result =>
    console.log(
      `${++count} ${result.finding.name} ${result.finding.resourceName}`
    )
  );
}
listFindingsAtTime();

Filter examples

The following sections show practical examples of finding filters.

Filter for findings that occurred after a point in time

These example filters match findings that most recently occurred after Wednesday, June 5, 2019 10:12:05 PM GMT. With the event_time filter, you can express time using the following formats and types:

  • Unix epoch time (in milliseconds) as an integer literal

    "event_time > 1559772725000"
    
  • RFC 3339 as a string literal

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

Filter on array-type fields

The following example shows the use of a partial string match on an array-type field within a filter:

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

The Security Command Center API returns any finding with a website.com partial string within the array. For example, it matches a finding with indicator.domains = [\"onewebsite.com\"] because "website.com" is a substring in an element in the array.

In the following sections, the example filters show some options for use of rich array-type filtering using the contains() function.

Filter on the vulnerability.cve.references field

The following example returns findings where at least one element in the vulnerability.cve.references array has both a source property equal to SOURCE_OF_REFERENCE and a uri property that has FILTERED_URI.

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

Replace the following:

Filter on the indicator.domains field

The following example returns findings where at least one indicator domain has both mycompanyprefix and .ca.

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

Filter on the indicator.ip_addresses field

The following example returns findings where at least one element in the indicator.ip_addresses array is equal to IP_ADDRESS.

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

Replace IP_ADDRESS with an IP address associated with the findings you're searching for.

Filter on external system assignees

The following example returns findings where at least one element in the external_systems.EXTERNAL_SYSTEM_NAME.assignees array is equal to ASSIGNEE.

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

Replace the following:

  • EXTERNAL_SYSTEM_NAME: the name of a third-party SIEM/SOAR system—for example, demisto.
  • ASSIGNEE: an assignee in the external system.

Filter on the resource.folders.resource_folder field

The following example returns findings where at least one element in the resource.folders.resource_folder array is not equal to FOLDER_NAME.

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

Filter on the resource.folders.resource_folder_display_name field

The following example returns findings where at least one element in the resource.folders.resource_folder_display_name array is equal to DISPLAY_NAME.

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

Replace DISPLAY_NAME with the user-defined name of the folder associated with the findings you're searching for.

To learn how to use a contains() function in a finding filter, see Filtering on array-type fields.

What's next

Learn more Setting up finding notifications.