Répertorier des résultats de sécurité à l'aide de l'API Security Command Center

Les résultats de Security Command Center modélisent les risques de sécurité potentiels des éléments d'un projet ou d'une organisation. Un résultat correspond toujours à un élément spécifique dans Security Command Center.

Ce guide explique comment accéder aux résultats à l'aide des bibliothèques clientes de Security Command Center. Chaque résultat appartient à une source. La plupart des détecteurs ou des fournisseurs de résultats produisent des résultats dans la même source.

Les rôles IAM pour Security Command Center peuvent être attribués au niveau de l'organisation, du dossier ou du projet. Votre capacité à afficher, modifier, créer ou mettre à jour les résultats, les éléments et les sources de sécurité dépend du niveau pour lequel vous disposez d'un accès. Pour en savoir plus sur les rôles Security Command Center, consultez la page Contrôle des accès.

Avant de commencer

Avant de configurer une source, vous devez effectuer les opérations suivantes :

Format de page

Toutes les API de liste Security Command Center sont paginées. Chaque réponse renvoie une page de résultats et un jeton pour renvoyer la page suivante. La taille de la page est configurable. La taille de page par défaut est de 10. Vous pouvez définir une valeur minimale de 1 et une valeur maximale de 1 000.

Conservation des résultats

Vous pouvez consulter ou interroger un résultat pendant au moins 13 mois.

Security Command Center stocke des instantanés de chaque résultat. Un instantané d'un résultat est conservé pendant au moins 13 mois. Si tous les instantanés d'un résultat sont supprimés, ce résultat ne peut plus être listé ni récupéré.

Pour en savoir plus sur la conservation des données de Security Command Center, consultez la section Conservation des données.

Répertorier toutes les données

gcloud

Pour répertorier toutes les conclusions d'un projet, d'un dossier ou d'une organisation, exécutez la commande suivante:

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

Remplacez les éléments suivants :

  • PARENT_TYPE: niveau de la hiérarchie des ressources pour lequel lister les résultats. Utilisez organizations, folders ou projects.
  • PARENT_ID: ID numérique de l'organisation, du dossier ou du projet, ou ID alphanumérique du projet.
  • LOCATION : si la résidence des données est activée, l'emplacement Security Command Center dans lequel répertorier les résultats. Si la résidence des données n'est pas activée, utilisez la valeur global.

Pour plus d'exemples, exécutez la commande suivante :

gcloud scc findings list --help

Pour consulter des exemples dans la documentation, consultez 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

Le résultat de chaque élément se présente comme suit :

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

Filtrer les résultats

Un projet, un dossier ou une organisation peuvent générer de nombreux résultats. L'exemple précédent n'utilise pas de filtre. Par conséquent, tous les enregistrements de résultats sont renvoyés.

Pour n'obtenir des informations que sur les champs qui vous intéressent, vous pouvez utiliser des filtres de recherche. Ces filtres sont semblables à des clauses "where" dans les instructions SQL, à ceci près qu'à la place des colonnes, ils s'appliquent aux objets renvoyés par l'API.

L'exemple suivant ne répertorie que les résultats possédant une catégorie "MEDIUM_RISK_ONE". Différents fournisseurs de résultats (également appelés sources de sécurité) utilisent différents ensembles de catégories. Pour déterminer les catégories que vous pouvez utiliser dans votre filtre, consultez la documentation du fournisseur de résultats.

gcloud

Utilisez la commande suivante pour filtrer les résultats:

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

Remplacez les éléments suivants :

  • PARENT_TYPE: niveau de la hiérarchie des ressources pour lequel lister les résultats. Utilisez organizations, folders ou projects.
  • PARENT_ID: ID numérique de l'organisation, du dossier ou du projet, ou ID alphanumérique du projet.
  • LOCATION : si la résidence des données est activée, l'emplacement Security Command Center dans lequel lister les résultats avec un filtre. Si la résidence des données n'est pas activée, utilisez la valeur global.
  • SOURCE_ID: ID de la source de sécurité qui fournit le type de résultat.
  • FILTER: filtre que vous devez utiliser. Par exemple, le filtre suivant renvoie uniquement les résultats de la catégorie MEDIUM_RISK_ONE:
    --filter="category=\"MEDIUM_RISK_ONE\""

Pour plus d'exemples, exécutez la commande suivante :

gcloud scc findings list --help

Pour consulter des exemples dans la documentation, consultez 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 est également compatible avec les tableaux et objets JSON entiers en tant que types de propriétés potentiels. Vous pouvez appliquer les filtres suivants :

  • Éléments de tableau
  • Objets JSON entiers avec correspondance de chaîne partielle dans l'objet
  • Sous-champs d'objets JSON

Opérateurs compatibles

Les instructions de requête pour les résultats de Security Command Center sont compatibles avec les opérateurs acceptés par la plupart des API Google Cloud.

La liste suivante montre l'utilisation de différents opérateurs:

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

La liste suivante répertorie tous les opérateurs et fonctions compatibles avec les instructions de requête pour les résultats:

  • Pour les chaînes :
    • = pour une égalité parfaite
    • : pour une correspondance de chaîne partielle
  • Pour les nombres :
    • <, >, <=, >= pour les inégalités
    • =, != pour l'égalité
  • Pour les valeurs booléennes :
    • = pour l'égalité
  • Pour les relations logiques :
    • AND
    • OR
    • NOT ou -
  • Pour les expressions de regroupement :
    • (, ) (parenthèses)
  • Pour les tableaux :
    • contains(), une fonction permettant d'interroger les résultats avec un champ de tableau contenant au moins un élément correspondant au filtre spécifié
    • containsOnly(), une fonction permettant d'interroger les résultats avec un champ de tableau ne contenant que les éléments correspondant au filtre spécifié
  • Pour les adresses IP :
    • inIpRange(), une fonction permettant d'interroger les adresses IP dans une plage CIDR spécifiée

Filtrage des adresses IP

Certaines propriétés de recherche incluent des adresses IP. Vous pouvez filtrer les résultats en fonction d'adresses IP spécifiques ou d'une plage d'adresses IP.

Les adresses IP apparaissent sous forme de chaînes dans divers résultats et propriétés de résultats, y compris les suivants:

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

Pour filtrer sur une adresse IP spécifique, vous pouvez utiliser l'opérateur d'égalité, comme illustré dans l'exemple suivant:

access.caller_ip="192.0.2.0"

Pour filtrer les résultats en fonction d'une plage d'adresses IP, utilisez la fonction inIpRange. Avec la fonction inIpRange, vous filtrez les résultats uniquement sur ceux qui contiennent une adresse IP dans une plage CIDR spécifiée. En utilisant l'opération NOT avec inIpRange, vous pouvez filtrer les résultats uniquement sur ceux qui contiennent une adresse IP en dehors de la plage CIDR spécifiée.

L'exemple suivant montre la syntaxe de la fonction inIpRange:

inIpRange(IP_FINDING_FIELD, "CIDR_RANGE")

Si l'adresse IP se trouve dans un élément de tableau dans un champ de résultat contenant un tableau, utilisez la syntaxe suivante avec la fonction contains et la fonction inIpRange:

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

Dans l'exemple suivant, la fonction inIpRange évalue chaque élément destination_ip du tableau contenu dans le champ de recherche connections pour une adresse IP située dans la plage CIDR définie par 192.0.2.0/24:

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

L'exemple suivant montre une commande de gcloud CLI qui utilise la fonction inIpRange pour filtrer les résultats dont l'adresse IP dans le champ connections.source_ip se trouve dans une plage, mais pas dans une autre. Le champ connections est un champ de type tableau. La fonction contains est donc utilisée:

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

Exemple d'objet JSON

Dans les exemples décrits ultérieurement sur cette page, nous partons du principe que l'objet JSON suivant est un attribut de résultat :

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

Exemple de filtrage de résultats

Supposons que l'exemple JSON précédent est un attribut de résultat nommé my_property. L'exemple suivant inclut des requêtes de résultats contenant l'objet en tant que propriété. Vous pouvez également associer ces filtres à d'autres filtres en utilisant AND et OR dans votre requête.

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

Sous-filtres pour les champs de type tableau

Lorsque vous appelez ListFindings, vous pouvez utiliser une correspondance de sous-chaîne :, qui effectue une seule vérification pour une correspondance de chaîne partielle sur l'ensemble du contenu du tableau. Vous pouvez également exécuter un sous-filtre directement sur les éléments du tableau et ses sous-champs à l'aide de l'une des fonctions suivantes:

  • La fonction contains() permet de renvoyer des résultats lorsqu'un ou plusieurs éléments du tableau contiennent la valeur spécifiée.

  • La fonction containsOnly() permet de ne renvoyer des résultats que si tous les éléments du tableau correspondent au sous-filtre.

Ces deux fonctions sont compatibles avec les fonctionnalités de requête de sous-filtres, comme les suivantes:

  • Correspondance exacte des éléments : fait correspondre les éléments d'un tableau contenant la chaîne exacte "example".
  • Opérations arithmétiques spécifiques : fait correspondre les éléments d'un tableau supérieurs ou égaux à 100.
  • Filtrage complexe en fonction des structures des tableaux : fait correspondre les éléments d'un tableau contenant la propriété x avec une valeur y correspondante.

Format de la fonction contains()

La fonction contains() a le format suivant :

contains(ARRAY_ATTRIBUTE_NAME, SUBFILTER)

Remplacez les éléments suivants :

  • ARRAY_ATTRIBUTE_NAME: champ ou sous-champ de type tableau (liste).
  • SUBFILTER: expression qui définit les valeurs à rechercher dans le tableau. Le format du sous-filtre diffère selon que ARRAY_ATTRIBUTE_NAME est un tableau d'objets ou un tableau d'éléments de type primitif. Si ARRAY_ATTRIBUTE_NAME est un tableau d'objets comportant des tableaux imbriqués, vous pouvez utiliser un sous-filtre spécifique pour indiquer que vous souhaitez que toutes les conditions soient remplies dans le même élément ARRAY_ATTRIBUTE_NAME.

L'API Security Command Center renvoie les résultats pour lesquels ARRAY_ATTRIBUTE_NAME contient au moins un élément correspondant au sous-filtre SUBFILTER.

Format de la fonction containsOnly()

La fonction containsOnly() a le format suivant :

containsOnly(ARRAY_ATTRIBUTE_NAME, SUBFILTER)

Remplacez les éléments suivants :

  • ARRAY_ATTRIBUTE_NAME: champ ou sous-champ de type tableau (liste). Lorsque vous exécutez des requêtes à l'aide de l'API Security Command Center, vous pouvez utiliser la fonction containsOnly() pour tout attribut de tableau disponible.
  • SUBFILTER: expression qui définit les valeurs à rechercher dans le tableau. Le format du sous-filtre diffère selon que ARRAY_ATTRIBUTE_NAME est un tableau d'objets ou un tableau d'éléments de type primitif. Si ARRAY_ATTRIBUTE_NAME est un tableau d'objets comportant des tableaux imbriqués, vous pouvez utiliser un sous-filtre spécifique pour indiquer que vous souhaitez que toutes les conditions soient remplies dans le même élément ARRAY_ATTRIBUTE_NAME.

L'API Security Command Center renvoie les résultats pour lesquels tous les éléments ARRAY_ATTRIBUTE_NAME correspondent à SUBFILTER.

Sous-filtre pour un tableau d'objets

Voici un extrait de l'exemple JSON précédent. Ici, le champ list_middle_object est un tableau d'objets :

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

L'exemple suivant interroge les résultats pour lesquels au moins un des éléments du champ list_middle_object comporte un sous-champ v dont la valeur est supérieure ou égale à 321 :

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

Pour obtenir des exemples pratiques utilisant les fonctions contains() et containsOnly(), consultez la section Résultats contenant des valeurs de tableau spécifiques.

Sous-filtre pour un tableau contenant des éléments de type primitif

Les types primitifs sont des chaînes, des nombres et des valeurs booléennes. Pour utiliser la fonction contains() sur un tableau contenant des types primitifs, vous devez utiliser le mot clé spécial elem.

Voici un extrait de l'exemple JSON précédent. Ici, le champ u est un tableau d'éléments de type primitif :

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

L'exemple suivant interroge les résultats pour lesquels au moins un des éléments du champ u est "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\")"

Pour obtenir des exemples pratiques utilisant la fonction contains(), consultez la section Résultats contenant des valeurs de tableau spécifiques.

Sous-filtre spécifique

Voici un extrait de l'exemple JSON précédent. Ici, le champ list_middle_object est un tableau d'objets. Les objets de ce tableau contiennent un tableau imbriqué.

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

L'exemple suivant interroge les résultats pour lesquels les deux conditions suivantes sont remplies dans le même élément list_middle_object :

  • La valeur du sous-champ v est supérieure ou égale à 321.
  • Le sous-champ w ne contient pas d'élément dont la propriété a est égale à 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))"

Pour obtenir des exemples pratiques utilisant la fonction contains(), consultez la section Résultats contenant des valeurs de tableau spécifiques.

Exemple de tri des résultats

Vous pouvez trier les résultats par sous-champs stricts de type primitif : chaînes, nombres et booléens. Supposons que l'exemple JSON précédent est un attribut de résultat nommé my_property. L'exemple suivant inclut des requêtes permettant de trier les champs de résultats. Le mot clé DESC indique que le champ suivant doit être trié par ordre décroissant. L'ordre par défaut est croissant.

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"

Exemples de filtres

Les sections suivantes présentent des exemples pratiques de filtres de résultats.

Filtrer les résultats générés après un moment précis

Ces exemples de filtres correspondent aux résultats les plus récents obtenus après le mercredi 5 juin 2019 à 22:12:05 GMT. Avec le filtre event_time, vous pouvez exprimer le temps en utilisant les formats et types suivants :

  • Epoch Unix (en millisecondes) sous forme de littéral entier

    "event_time > 1559772725000"
    
  • RFC 3339 en tant que littéral de chaîne

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

Filtrer les champs de type tableau

L'exemple suivant décrit l'utilisation d'une correspondance de chaîne partielle sur un champ de type tableau dans un filtre :

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

L'API Security Command Center renvoie tous les résultats avec une chaîne partielle website.com dans le tableau. Par exemple, il fait correspondre un résultat avec indicator.domains = [\"onewebsite.com\"], car "website.com" est une sous-chaîne dans un élément du tableau.

Dans les sections suivantes, les exemples de filtres montrent certaines options d'utilisation d'un filtre complet de type tableau à l'aide de la fonction contains().

Filtrer le champ vulnerability.cve.references

L'exemple suivant renvoie les résultats pour lesquels au moins un élément du tableau vulnerability.cve.references possède une propriété source égale à SOURCE_OF_REFERENCE et une propriété uri contenant FILTERED_URI.

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

Remplacez les éléments suivants :

Filtrer le champ indicator.domains

L'exemple suivant renvoie les résultats pour lesquels au moins un domaine d'indicateur contient à la fois mycompanyprefix et .ca.

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

Filtrer le champ indicator.ip_addresses

L'exemple suivant renvoie les résultats pour lesquels au moins un élément du tableau indicator.ip_addresses est égal à IP_ADDRESS.

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

Remplacez IP_ADDRESS par une adresse IP associée aux résultats que vous recherchez.

Filtrer les personnes responsables des systèmes externes

L'exemple suivant renvoie les résultats pour lesquels au moins un élément du tableau external_systems.EXTERNAL_SYSTEM_NAME.assignees est égal à ASSIGNEE.

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

Remplacez les éléments suivants :

  • EXTERNAL_SYSTEM_NAME: nom d'un système SIEM/SOAR tiers, par exemple demisto.
  • ASSIGNEE : personne responsable d'un système externe.

Filtrer le champ resource.folders.resource_folder

L'exemple suivant renvoie les résultats pour lesquels au moins un élément du tableau resource.folders.resource_folder n'est pas égal à FOLDER_NAME.

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

Filtrer le champ resource.folders.resource_folder_display_name

L'exemple suivant renvoie les résultats pour lesquels au moins un élément du tableau resource.folders.resource_folder_display_name est égal à DISPLAY_NAME.

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

Remplacez DISPLAY_NAME par le nom défini par l'utilisateur pour le dossier associé aux résultats que vous recherchez.

Le filtre n'inclut que des comptes de service spécifiques

L'exemple suivant ne renvoie des résultats que lorsque la valeur de membre de chaque entrée iam_bindings est égale à l'un des comptes de service fournis.

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

Remplacez SERVICE_ACCOUNT1, SERVICE_ACCOUNT2 et SERVICE_ACCOUNT3 par les adresses e-mail des comptes de service.

Pour apprendre à utiliser les fonctions contains() et containsOnly() dans un filtre de résultats, consultez la section Sous-filtres pour les champs de type tableau.

Étape suivante

Découvrez comment configurer des notifications de résultats.