Managing security marks using the Security Command Center API

This guide describes how to use the Security Command Center Security Command Center API to manage security marks. Security marks, or "marks", are customizable annotations on assets or findings in Security Command Center that let you add your own business context to these objects.

Before you begin

Before you can work with security marks, you need to Set up a service account and SDK.

To add or change security marks, you must have an Identity and Access Management role that includes permissions for the kind of mark that you want to use:

  • Asset marks: Asset Security Marks Writer, securitycenter.assetSecurityMarksWriter
  • Finding marks: Finding Security Marks Writer, securitycenter.findingSecurityMarksWriter

For more information on IAM roles in Security Command Center, see Access control. To learn how to use security marks effectively, see Using Security Command Center security marks.

Adding or updating security marks on assets

When using the Security Command Center API, adding and updating security marks are the same operation. The example below shows how to add security marks for two key value pairs (key_a, value_a) and (key_b, value_b).

The following code uses field masks to ensure that only those values are updated. If field masks aren't provided, all security marks are cleared before adding the given keys and values.

gcloud

  # ORGANIZATION=12344321
  # ASSET=43211234
  SECURITY_MARKS="key_a=value_a,key_b=value_b"
  UPDATE_MASK="marks.key_a,marks.key_b"

  gcloud scc assets update-marks $ASSET \
      --organization $ORGANIZATION \
      --security-marks $SECURITY_MARKS \
      --update-mask $UPDATE_MASK

For more examples, run:

  gcloud scc assets update-marks --help

Python

from google.cloud import securitycenter
from google.protobuf import field_mask_pb2

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

# asset_name is the resource path for an asset that exists in CSCC.
# Its format is "organization/{organization_id}/assets/{asset_id}
# e.g.:
# asset_name = organizations/123123342/assets/12312321
marks_name = f"{asset_name}/securityMarks"

# Notice the suffix after "marks." in the field mask matches the keys
# in marks.
field_mask = field_mask_pb2.FieldMask(paths=["marks.key_a", "marks.key_b"])
marks = {"key_a": "value_a", "key_b": "value_b"}

updated_marks = client.update_security_marks(
    request={
        "security_marks": {"name": marks_name, "marks": marks},
        "update_mask": field_mask,
    }
)
print(updated_marks)

Java

static SecurityMarks addToAsset(String assetName) {
  try (SecurityCenterClient client = SecurityCenterClient.create()) {
    // String assetName = "organizations/123123342/assets/12312321";
    // Start setting up a request to add security marks for an asset.
    ImmutableMap markMap = ImmutableMap.of("key_a", "value_a", "key_b", "value_b");

    // Add security marks and field mask for security marks.
    SecurityMarks securityMarks =
        SecurityMarks.newBuilder()
            .setName(assetName + "/securityMarks")
            .putAllMarks(markMap)
            .build();
    FieldMask updateMask =
        FieldMask.newBuilder().addPaths("marks.key_a").addPaths("marks.key_b").build();

    UpdateSecurityMarksRequest request =
        UpdateSecurityMarksRequest.newBuilder()
            .setSecurityMarks(securityMarks)
            .setUpdateMask(updateMask)
            .build();

    // Call the API.
    SecurityMarks response = client.updateSecurityMarks(request);

    System.out.println("Security Marks:");
    System.out.println(response);
    return response;
  } 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/genproto/protobuf/field_mask"
)

// addSecurityMarks adds/updates the security marks for the assetName.
// Specifically, it sets "key_a" and "key_b" to "value_a" and "value_b"
// respectively.  assetName is the resource path for an asset.
func addSecurityMarks(w io.Writer, assetName string) error {
	// assetName := "organizations/123123342/assets/12312321"
	// 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.UpdateSecurityMarksRequest{
		// If not set or empty, all marks would be cleared before
		// adding the new marks below.
		UpdateMask: &field_mask.FieldMask{
			Paths: []string{"marks.key_a", "marks.key_b"},
		},
		SecurityMarks: &securitycenterpb.SecurityMarks{
			Name: fmt.Sprintf("%s/securityMarks", assetName),
			// Note keys correspond to the last part of each path.
			Marks: map[string]string{"key_a": "value_a", "key_b": "value_b"},
		},
	}
	updatedMarks, err := client.UpdateSecurityMarks(ctx, req)
	if err != nil {
		return fmt.Errorf("UpdateSecurityMarks: %w", err)
	}

	fmt.Fprintf(w, "Updated marks: %s\n", updatedMarks.Name)
	for k, v := range updatedMarks.Marks {
		fmt.Fprintf(w, "%s = %s\n", k, v)
	}
	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();

async function addSecurityMarks() {
  // assetName is the full resource path for the asset to update.
  /*
   * TODO(developer): Uncomment the following lines
   */
  // const assetName = "organizations/123123342/assets/12312321";
  const [newMarks] = await client.updateSecurityMarks({
    securityMarks: {
      name: `${assetName}/securityMarks`,
      marks: {key_a: 'value_a', key_b: 'value_b'},
    },
    // Only update the marks with these keys.
    updateMask: {paths: ['marks.key_a', 'marks.key_b']},
  });

  console.log('New marks: %', newMarks);
}
addSecurityMarks();

Read Managing policies for information on dedicated asset marks for Security Health Analytics detectors.

Deleting security marks on assets

Deleting specific security marks is accomplished in a similar fashion to adding or updating them, specifically calling update with a field mask but without any corresponding value. In the example below, security marks with keys key_a and key_b are deleted.

gcloud

  # ORGANIZATION=12344321
  # ASSET=43211234
  UPDATE_MASK="marks.key_a,marks.key_b"

  gcloud scc assets update-marks $ASSET \
      --organization $ORGANIZATION \
      --update-mask $UPDATE_MASK

For more examples, run:

  gcloud scc assets update-marks --help

Python

from google.cloud import securitycenter
from google.protobuf import field_mask_pb2

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

# asset_name is the resource path for an asset that exists in CSCC.
# Its format is "organization/{organization_id}/assets/{asset_id}
# e.g.:
# asset_name = organizations/123123342/assets/12312321
marks_name = f"{asset_name}/securityMarks"

field_mask = field_mask_pb2.FieldMask(paths=["marks.key_a", "marks.key_b"])

updated_marks = client.update_security_marks(
    request={
        "security_marks": {
            "name": marks_name
            # Note, no marks specified, so the specified values in
            # the fields masks will be deleted.
        },
        "update_mask": field_mask,
    }
)
print(updated_marks)

Java

static SecurityMarks clearFromAsset(String assetName) {
  // String assetName = "organizations/123123342/assets/12312321";
  try (SecurityCenterClient client = SecurityCenterClient.create()) {
    // Start setting up a request to clear security marks for an asset.
    // Create security mark and field mask for clearing security marks.
    SecurityMarks securityMarks =
        SecurityMarks.newBuilder().setName(assetName + "/securityMarks").build();
    FieldMask updateMask =
        FieldMask.newBuilder().addPaths("marks.key_a").addPaths("marks.key_b").build();

    UpdateSecurityMarksRequest request =
        UpdateSecurityMarksRequest.newBuilder()
            .setSecurityMarks(securityMarks)
            .setUpdateMask(updateMask)
            .build();

    // Call the API.
    SecurityMarks response = client.updateSecurityMarks(request);

    System.out.println("Security Marks cleared:");
    System.out.println(response);
    return response;
  } 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/genproto/protobuf/field_mask"
)

// deleteSecurityMarks deletes security marks "key_a" and  "key_b" from
// assetName's marks. assetName is the resource path for an asset.
func deleteSecurityMarks(w io.Writer, assetName string) error {
	// assetName := "organizations/123123342/assets/12312321"
	// 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.UpdateSecurityMarksRequest{
		// If not set or empty, all marks would be cleared.
		UpdateMask: &field_mask.FieldMask{
			Paths: []string{"marks.key_a", "marks.key_b"},
		},
		SecurityMarks: &securitycenterpb.SecurityMarks{
			Name: fmt.Sprintf("%s/securityMarks", assetName),
			// Intentionally not setting marks with the
			// corresponding field mask deletes them.
		},
	}
	updatedMarks, err := client.UpdateSecurityMarks(ctx, req)
	if err != nil {
		return fmt.Errorf("UpdateSecurityMarks: %w", err)
	}

	fmt.Fprintf(w, "Updated marks: %s\n", updatedMarks.Name)
	for k, v := range updatedMarks.Marks {
		fmt.Fprintf(w, "%s = %s\n", k, v)
	}
	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();

async function deleteSecurityMarks() {
  // assetName is the full resource path for the asset to update.
  /*
   * TODO(developer): Uncomment the following lines
   */
  // const assetName = "organizations/123123342/assets/12312321";
  const [newMarks] = await client.updateSecurityMarks({
    securityMarks: {
      name: `${assetName}/securityMarks`,
      // Intentionally, not setting marks to delete them.
    },
    // Only delete marks for the following keys.
    updateMask: {paths: ['marks.key_a', 'marks.key_b']},
  });

  console.log('Updated marks: %j', newMarks);
}
deleteSecurityMarks();

Adding and deleting security marks in the same request

The technique to add and update security marks and deleting security marks can be combined into the same request. In the example below, key_a is updated while key_b is deleted.

gcloud

  # ORGANIZATION=12344321
  # ASSET=43211234
  SECURITY_MARKS="key_a=new_value_for_a"
  UPDATE_MASK="marks.key_a,marks.key_b"

  gcloud scc assets update-marks $ASSET \
      --organization $ORGANIZATION \
      --security-marks $SECURITY_MARKS \
      --update-mask $UPDATE_MASK

For more examples, run:

  gcloud scc assets update-marks --help

gcloud

  # ORGANIZATION=12344321
  # ASSET=43211234
  SECURITY_MARKS="key_a=new_value_for_a"
  UPDATE_MASK="marks.key_a,marks.key_b"

  gcloud scc assets update-marks $ASSET \
      --organization $ORGANIZATION \
      --security-marks $SECURITY_MARKS \
      --update-mask $UPDATE_MASK

For more examples, run:

  gcloud scc assets update-marks --help

Python

from google.cloud import securitycenter
from google.protobuf import field_mask_pb2

client = securitycenter.SecurityCenterClient()
# asset_name is the resource path for an asset that exists in CSCC.
# Its format is "organization/{organization_id}/assets/{asset_id}
# e.g.:
# asset_name = organizations/123123342/assets/12312321
marks_name = f"{asset_name}/securityMarks"

field_mask = field_mask_pb2.FieldMask(paths=["marks.key_a", "marks.key_b"])
marks = {"key_a": "new_value_for_a"}

updated_marks = client.update_security_marks(
    request={
        "security_marks": {"name": marks_name, "marks": marks},
        "update_mask": field_mask,
    }
)
print(updated_marks)

Java

static SecurityMarks deleteAndUpdateMarks(String assetName) {
  // String assetName = "organizations/123123342/assets/12312321";
  try (SecurityCenterClient client = SecurityCenterClient.create()) {
    // Start setting up a request to clear and update security marks for an asset.
    // Create security mark and field mask for clearing security marks.
    SecurityMarks securityMarks =
        SecurityMarks.newBuilder()
            .setName(assetName + "/securityMarks")
            .putMarks("key_a", "new_value_for_a")
            .build();
    FieldMask updateMask =
        FieldMask.newBuilder().addPaths("marks.key_a").addPaths("marks.key_b").build();

    UpdateSecurityMarksRequest request =
        UpdateSecurityMarksRequest.newBuilder()
            .setSecurityMarks(securityMarks)
            .setUpdateMask(updateMask)
            .build();

    // Call the API.
    SecurityMarks response = client.updateSecurityMarks(request);

    System.out.println("Security Marks updated and cleared:");
    System.out.println(response);
    return response;
  } 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/genproto/protobuf/field_mask"
)

// addDeleteSecurityMarks adds/updates "key_a" and deletes  "key_b" from
// assetName's securityMarks. assetName is the resource path for an asset.
func addDeleteSecurityMarks(w io.Writer, assetName string) error {
	// assetName := "organizations/123123342/assets/12312321"
	// 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.UpdateSecurityMarksRequest{
		// If not set or empty, all marks would be cleared.
		UpdateMask: &field_mask.FieldMask{
			Paths: []string{"marks.key_a", "marks.key_b"},
		},
		SecurityMarks: &securitycenterpb.SecurityMarks{
			Name: fmt.Sprintf("%s/securityMarks", assetName),
			// Intentionally not setting marks for key_b to
			// delete it.
			Marks: map[string]string{"key_a": "new_value_a"},
		},
	}

	updatedMarks, err := client.UpdateSecurityMarks(ctx, req)
	if err != nil {
		return fmt.Errorf("UpdateSecurityMarks: %w", err)
	}

	fmt.Fprintf(w, "Updated marks: %s\n", updatedMarks.Name)
	for k, v := range updatedMarks.Marks {
		fmt.Fprintf(w, "%s = %s\n", k, v)
	}
	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();

async function addDeleteSecurityMarks() {
  // assetName is the full resource path for the asset to update.
  /*
   * TODO(developer): Uncomment the following lines
   */
  // assetName = "organizations/123123342/assets/12312321";
  const [newMarks] = await client.updateSecurityMarks({
    securityMarks: {
      name: `${assetName}/securityMarks`,
      marks: {key_a: 'new_value_a'},
    },
    // Only update the enableAssetDiscovery field.
    updateMask: {paths: ['marks.key_a', 'marks.key_b']},
  });

  console.log('New marks: %j', newMarks);
}
addDeleteSecurityMarks();

Adding Security Marks to Findings

Adding, updating, and deleting security marks on findings follows the same process as updating security marks on assets. The only change is the name of the resource used in the API call. Instead of an asset resource, you provide a finding resource name.

For example, to update security marks on a finding, use the following code:

gcloud

  # ORGANIZATION=12344321
  # SOURCE=43211234
  # FINDING_ID=testfindingid
  SECURITY_MARKS="key_a=value_a,key_b=value_b"
  UPDATE_MASK="marks.key_a,marks.key_b"

  gcloud scc findings update-marks $FINDING_ID \
      --source $SOURCE \
      --organization $ORGANIZATION \
      --security-marks $SECURITY_MARKS \
      --update-mask $UPDATE_MASK

For more examples, run:

  gcloud scc findings update-marks --help

Python

from google.cloud import securitycenter
from google.protobuf import field_mask_pb2

client = securitycenter.SecurityCenterClient()
# finding_name is the resource path for a finding that exists in CSCC.
# Its format is
# "organizations/{org_id}/sources/{source_id}/findings/{finding_id}"
# e.g.:
# finding_name = "organizations/1112/sources/1234/findings/findingid"
finding_marks_name = f"{finding_name}/securityMarks"

# Notice the suffix after "marks." in the field mask matches the keys
# in marks.
field_mask = field_mask_pb2.FieldMask(
    paths=["marks.finding_key_a", "marks.finding_key_b"]
)
marks = {"finding_key_a": "value_a", "finding_key_b": "value_b"}

updated_marks = client.update_security_marks(
    request={
        "security_marks": {"name": finding_marks_name, "marks": marks},
        "update_mask": field_mask,
    }
)

Java

static SecurityMarks addToFinding(FindingName findingName) {
  // FindingName findingName = FindingName.of(/*organization=*/"123234324",
  // /*source=*/"423432321", /*findingId=*/"samplefindingid2");
  try (SecurityCenterClient client = SecurityCenterClient.create()) {
    // Start setting up a request to add security marks for a finding.
    ImmutableMap markMap = ImmutableMap.of("key_a", "value_a", "key_b", "value_b");

    // Add security marks and field mask for security marks.
    SecurityMarks securityMarks =
        SecurityMarks.newBuilder()
            .setName(findingName + "/securityMarks")
            .putAllMarks(markMap)
            .build();
    FieldMask updateMask =
        FieldMask.newBuilder().addPaths("marks.key_a").addPaths("marks.key_b").build();

    UpdateSecurityMarksRequest request =
        UpdateSecurityMarksRequest.newBuilder()
            .setSecurityMarks(securityMarks)
            .setUpdateMask(updateMask)
            .build();

    // Call the API.
    SecurityMarks response = client.updateSecurityMarks(request);

    System.out.println("Security Marks:");
    System.out.println(response);
    return response;
  } 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/genproto/protobuf/field_mask"
)

// addSecurityMarks adds/updates a the security marks for the findingName and
// returns the updated marks. Specifically, it sets "key_a" an "key_b" to
// "value_a" and "value_b" respectively. findingName is the resource path for
// the finding to add marks to.
func addSecurityMarks(w io.Writer, findingName string) error {
	// findingName := "organizations/11123213/sources/12342342/findings/fidningid"
	// 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.UpdateSecurityMarksRequest{
		// If not set or empty, all marks would be cleared before
		// adding the new marks below.
		UpdateMask: &field_mask.FieldMask{
			Paths: []string{"marks.key_a", "marks.key_b"},
		},
		SecurityMarks: &securitycenterpb.SecurityMarks{
			Name: fmt.Sprintf("%s/securityMarks", findingName),
			// Note keys correspond to the last part of each path.
			Marks: map[string]string{"key_a": "value_a", "key_b": "value_b"},
		},
	}

	updatedMarks, err := client.UpdateSecurityMarks(ctx, req)
	if err != nil {
		return fmt.Errorf("UpdateSecurityMarks: %w", err)
	}

	fmt.Fprintf(w, "Updated marks: %s\n", updatedMarks.Name)
	for k, v := range updatedMarks.Marks {
		fmt.Fprintf(w, "%s = %s\n", k, v)
	}
	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();

async function addFindingSecurityMarks() {
  // findingName is the full resource path for the finding to update.
  /*
   * TODO(developer): Uncomment the following lines
   */
  // const findingName =
  // "organizations/123123342/sources/1213/findings/findingid";
  const [newMarks] = await client.updateSecurityMarks({
    securityMarks: {
      name: `${findingName}/securityMarks`,
      marks: {key_a: 'value_a', key_b: 'value_b'},
    },
    // Only update the marks with these keys.
    updateMask: {paths: ['marks.key_a', 'marks.key_b']},
  });

  console.log('New marks: %j', newMarks);
}
addFindingSecurityMarks();

Security marks are processed during batch scans—which run twice daily—and not in real time. There may be a delay of 12 to 24 hours before security marks are processed and enforcement polices that resolve or reopen findings are applied.

Listing assets with security mark filters

After security marks are set on an asset, they can be used in the filter argument to the ListAssets API call. For example, to query for all assets where key_a = value_a, use the following code:

gcloud

  # ORGANIZATION=12344321
  FILTER="security_marks.marks.key_a = \"value_a\""

  gcloud scc assets list $ORGANIZATION \
      --filter "$FILTER"

For more examples, run:

  gcloud scc assets list --help

Python

from google.cloud import securitycenter

client = securitycenter.SecurityCenterClient()

# organization_id is the numeric ID of the organization.
# organization_id=1234567777
org_name = f"organizations/{organization_id}"

marks_filter = 'security_marks.marks.key_a = "value_a"'
# Call the API and print results.
asset_iterator = client.list_assets(
    request={"parent": org_name, "filter": marks_filter}
)

# Call the API and print results.
asset_iterator = client.list_assets(
    request={"parent": org_name, "filter": marks_filter}
)
for i, asset_result in enumerate(asset_iterator):
    print(i, asset_result)

Java

static ImmutableList<ListAssetsResult> listAssetsWithQueryMarks(
    OrganizationName organizationName) {
  try (SecurityCenterClient client = SecurityCenterClient.create()) {
    // Start setting up a request for to list all assets filtered by a specific security mark.
    // OrganizationName organizationName = OrganizationName.of(/*organizationId=*/"123234324");
    ListAssetsRequest request =
        ListAssetsRequest.newBuilder()
            .setParent(organizationName.toString())
            .setFilter("security_marks.marks.key_a = \"value_a\"")
            .build();

    // Call the API.
    ListAssetsPagedResponse response = client.listAssets(request);

    // This creates one list for all assets.  If your organization has a large number of assets
    // this can cause out of memory issues.  You can process them batches by returning
    // the Iterable returned response.iterateAll() directly.
    ImmutableList<ListAssetsResult> results = ImmutableList.copyOf(response.iterateAll());
    System.out.println("Assets with security mark - key_a=value_a:");
    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"
)

// listAssetsWithMarks prints assets that have a mark of key_a equal to value_a
// to w for orgID.  orgID is the numeric Organization ID.
func listAssetsWithMarks(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.ListAssetsRequest{
		Parent: fmt.Sprintf("organizations/%s", orgID),
		Filter: `security_marks.marks.key_a = "value_a"`,
	}

	it := client.ListAssets(ctx, req)
	for {
		result, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return fmt.Errorf("ListAssets: %w", err)
		}
		asset := result.Asset
		properties := asset.SecurityCenterProperties
		fmt.Fprintf(w, "Asset Name: %s, ", asset.Name)
		fmt.Fprintf(w, "Resource Name %s, ", properties.ResourceName)
		fmt.Fprintf(w, "Resource Type %s\n", properties.ResourceType)
	}
	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";
const orgName = client.organizationPath(organizationId);

// Call the API with automatic pagination.
async function listAssetsWithSecurityMarks() {
  const [response] = await client.listAssets({
    parent: orgName,
    filter: 'security_marks.marks.key_a="value_a"',
  });
  let count = 0;
  Array.from(response).forEach(result =>
    console.log(
      `${++count} ${result.asset.name} ${
        result.asset.securityCenterProperties.resourceName
      }`
    )
  );
}

listAssetsWithSecurityMarks();

Listing findings with security mark filters

After security marks are set on a finding, they can be used in the filter argument to the ListFindings API call. For example, to query all assets where key_a != value_a, use the following code:

gcloud

  # ORGANIZATION=12344321
  # SOURCE=43211234
  FILTER="NOT security_marks.marks.key_a=\"value_a\""

  gcloud scc findings list $ORGANIZATION \
      --source $SOURCE \
      --filter "$FILTER"

For more examples, run:

  gcloud scc findings list --help

Python

from google.cloud import securitycenter

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"
marks_filter = 'NOT security_marks.marks.finding_key_a="value_a"'

# Call the API and print results.
finding_iterator = client.list_findings(
    request={"parent": source_name, "filter": marks_filter}
)
for i, finding_result in enumerate(finding_iterator):
    print(i, finding_result)

Java

static ImmutableList<ListFindingsResult> listFindingsWithQueryMarks(SourceName sourceName) {
  try (SecurityCenterClient client = SecurityCenterClient.create()) {
    // Start setting up a request for to list all findings filtered by a specific security mark.
    // SourceName sourceName = SourceName.of(/*organization=*/"123234324",/*source=*/
    // "423432321");

    String filter = "NOT security_marks.marks.key_a=\"value_a\"";

    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 in the filter.If your organization has a large
    // number of
    // findings this can cause out of memory issues.  You can process them batches by returning
    // the Iterable returned response.iterateAll() directly.
    ImmutableList<ListFindingsResult> results = ImmutableList.copyOf(response.iterateAll());
    System.out.println("Findings with security mark - key_a=value_a:");
    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"
)

// listFindingsWithMarks prints findings that don't have a security mark
// key_a equal to value_a to w.  sourceName is the full resource name
// of the source to search for findings under.
func listFindingsWithMarks(w io.Writer, sourceName string) error {
	// sourceName := "organizations/111122222444/sources/1234"
	// 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: `NOT security_marks.marks.key_a="value_a"`,
	}
	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 = "organizations/111122222444/sources/1234";

async function listFindingsWithSecurityMarks() {
  const [response] = await client.listFindings({
    // List findings across all sources.
    parent: sourceName,
    filter: 'NOT security_marks.marks.key_a="value_a"',
  });
  let count = 0;
  Array.from(response).forEach(result =>
    console.log(
      `${++count} ${result.finding.name} ${result.finding.resourceName}`
    )
  );
}
listFindingsWithSecurityMarks();

What's next