使用 Security Command Center API 管理发现结果

本指南将介绍如何使用 Security Command Center API 创建和更新发现结果。

准备工作

在创建和更新发现结果之前,您需要完成以下操作:

为完成本指南,必须拥有组织级别的 Identity and Access Management (IAM) Security Center Findings Editor (securitycenter.findingsEditor) 角色。如需详细了解 Security Command Center 角色,请参阅访问权限控制

如果要使用安全标志创建发现结果,还必须拥有包含要使用的标记类型的权限的 IAM 角色:

  • Asset Security Marks Writer (securitycenter.assetSecurityMarksWriter)
  • Finding Security Marks Writer (securitycenter.findingSecurityMarksWriter)

如需详细了解标记,请参阅 使用 Security Command Center 安全标记

创建发现结果

为来源创建活跃的发现结果。

gcloud

gcloud scc findings create FINDING_NAME \
    --organization=PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --state=STATE \
    --category=CATEGORY \
    --event-time=EVENT_TIME \
    --resource-name=RESOURCE_NAME

替换以下内容:

  • FINDING_NAME:发现结果的名称。
  • PARENT_ID:父级组织的数字 ID。
  • LOCATION:如果数据驻留处于启用状态,则为创建发现结果时使用的 Security Command Center 位置;如果数据驻留处于停用状态,则使用值 global
  • SOURCE_ID:发现结果的来源的数字 ID。
  • STATE:发现结果的状态;如果发现结果需要关注,请使用 ACTIVE;如果发现结果已得到解决,请使用 INACTIVE
  • CATEGORY:相应发现所属的分类群组;例如 AUDIT_LOGGING_DISABLED
  • EVENT_TIME:事件发生的时间,采用 RFC 822 时间戳或其他 gcloud CLI 支持的时间戳格式
  • RESOURCE_NAME:相应发现适用的资源的完整资源名称。

Go

import (
	"context"
	"fmt"
	"io"
	"time"

	securitycenter "cloud.google.com/go/securitycenter/apiv2"
	"cloud.google.com/go/securitycenter/apiv2/securitycenterpb"
	"github.com/golang/protobuf/ptypes"
)

// createFinding demonstrates how to create a new security finding in CSCC.
// sourceName is the full resource name of the source the finding should
// be associated with.
func createFinding(w io.Writer, sourceName string) error {
	// sourceName := "organizations/111122222444/sources/1234/locations/global"
	// 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.
	// Use now as the eventTime for the security finding.
	eventTime, err := ptypes.TimestampProto(time.Now())
	if err != nil {
		return fmt.Errorf("TimestampProto: %w", err)
	}

	req := &securitycenterpb.CreateFindingRequest{
		Parent:    sourceName,
		FindingId: "samplefindingid",
		Finding: &securitycenterpb.Finding{
			State: securitycenterpb.Finding_ACTIVE,
			// Resource the finding is associated with. This is an
			// example any resource identifier can be used.
			ResourceName: "//cloudresourcemanager.googleapis.com/organizations/11232/sources/-/locations/global",
			// A free-form category.
			Category: "MEDIUM_RISK_ONE",
			// The time associated with discovering the issue.
			EventTime: eventTime,
		},
	}
	finding, err := client.CreateFinding(ctx, req)
	if err != nil {
		return fmt.Errorf("CreateFinding: %w", err)
	}
	fmt.Fprintf(w, "New finding created: %s\n", finding.Name)
	fmt.Fprintf(w, "Event time (Epoch Seconds): %d\n", eventTime.Seconds)
	return nil
}

Java


import com.google.cloud.securitycenter.v2.CreateFindingRequest;
import com.google.cloud.securitycenter.v2.Finding;
import com.google.cloud.securitycenter.v2.Finding.FindingClass;
import com.google.cloud.securitycenter.v2.Finding.Mute;
import com.google.cloud.securitycenter.v2.Finding.Severity;
import com.google.cloud.securitycenter.v2.Finding.State;
import com.google.cloud.securitycenter.v2.SecurityCenterClient;
import com.google.cloud.securitycenter.v2.SourceName;
import com.google.protobuf.Timestamp;
import java.io.IOException;
import java.time.Instant;
import java.util.Optional;
import java.util.UUID;

public class CreateFindings {

  public static void main(String[] args) throws IOException {
    // TODO: Replace the sample resource name
    // organizationId: Google Cloud Organization id.
    String organizationId = "{google-cloud-organization-id}";

    // Specify the location to list the findings.
    String location = "global";

    // The source id corresponding to the finding.
    String sourceId = "{source-id}";

    // The finding id.
    String findingId = "testfindingv2" + UUID.randomUUID().toString().split("-")[0];

    // Specify the category.
    Optional<String> category = Optional.of("MEDIUM_RISK_ONE");

    createFinding(organizationId, location, findingId, sourceId, category);
  }

  /**
   * Creates a security finding within a specific source in the Security Command Center.
   */
  public static Finding createFinding(String organizationId, String location, String findingId,
      String sourceId, Optional<String> category) throws IOException {
    try (SecurityCenterClient client = SecurityCenterClient.create()) {
      // Optionally SourceName or String can be used.
      // String sourceName = String.format("organizations/%s/sources/%s", organizationId, sourceId);
      SourceName sourceName = SourceName.of(organizationId, sourceId);

      Instant eventTime = Instant.now();
      // The resource this finding applies to. The Cloud Security Command Center UI can link the
      // findings for a resource to the corresponding asset of a resource if there are matches.
      String resourceName = String.format("//cloudresourcemanager.googleapis.com/organizations/%s",
          organizationId);

      // Set up a request to create a finding in a source.
      String parent = String.format("%s/locations/%s", sourceName.toString(), location);
      Finding finding = Finding.newBuilder()
          .setParent(parent)
          .setState(State.ACTIVE)
          .setSeverity(Severity.LOW)
          .setMute(Mute.UNMUTED)
          .setFindingClass(FindingClass.OBSERVATION)
          .setResourceName(resourceName)
          .setEventTime(Timestamp.newBuilder()
              .setSeconds(eventTime.getEpochSecond())
              .setNanos(eventTime.getNano()))
          .setCategory(category.orElse("LOW_RISK_ONE"))
          .build();

      CreateFindingRequest createFindingRequest = CreateFindingRequest.newBuilder()
          .setParent(parent)
          .setFindingId(findingId)
          .setFinding(finding).build();

      // Call the API.
      Finding response = client.createFinding(createFindingRequest);
      return response;
    }
  }
}

Node.js

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

// Create a Security Center client
const client = new SecurityCenterClient();

// TODO(developer): Update the following for your own environment.
const organizationId = '1081635000895';
const location = 'global';

const [source] = await client.createSource({
  source: {
    displayName: 'Customized Display Name V2',
    description: 'A new custom source that does X',
  },
  parent: client.organizationPath(organizationId),
});

const sourceId = source.name.split('/')[3];

// Resource name of the new finding's parent. Examples:
//  - `organizations/[organization_id]/sources/[source_id]`
//  - `organizations/[organization_id]/sources/[source_id]/locations/[location_id]`
const parent = `organizations/${organizationId}/sources/${sourceId}/locations/${location}`;

// The resource this finding applies to. The Cloud Security Command Center UI can link the
// findings for a resource to the corresponding asset of a resource if there are matches.
const resourceName = `//cloudresourcemanager.googleapis.com/organizations/${organizationId}`;

// Unique identifier provided by the client within the parent scope.
// It must be alphanumeric and less than or equal to 32 characters and
// greater than 0 characters in length.
const findingId = uuid.v4().replace(/-/g, '');

// Get the current timestamp.
const eventDate = new Date();

// Finding category.
const category = 'MEDIUM_RISK_ONE';

// Build the finding request object.
const createFindingRequest = {
  parent: parent,
  findingId: findingId,
  finding: {
    resourceName,
    category,
    state: 'ACTIVE',
    // The time associated with discovering the issue.
    eventTime: {
      seconds: Math.floor(eventDate.getTime() / 1000),
      nanos: (eventDate.getTime() % 1000) * 1e6,
    },
  },
};

// Call the API.
const [finding] = await client.createFinding(createFindingRequest);
console.log('New finding created: %j', finding);

Python

def create_finding(
    organization_id, location_id, finding_id, source_name, category
) -> Dict:
    """
    cretaes a new finding
    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
        finding_id: unique identifier provided by the client.
        location_id: GCP location id; example: 'global'
        category: the additional category group with in findings.
    Returns:
         Dict: returns the created findings details.
    """
    import datetime

    from google.cloud import securitycenter_v2
    from google.cloud.securitycenter_v2 import Finding

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

    # Use the current time as the finding "event time".
    event_time = datetime.datetime.now(tz=datetime.timezone.utc)

    # '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"
    # source_name = f"organizations/{organization_id}/sources/{source_name}"
    # category= "MEDIUM_RISK_ONE"
    # The resource this finding applies to.  The CSCC UI can link
    # the findings for a resource to the corresponding Asset of a resource
    # if there are matches.
    resource_name = (
        f"//cloudresourcemanager.googleapis.com/organizations/{organization_id}"
    )

    finding = Finding(
        state=Finding.State.ACTIVE,
        resource_name=resource_name,
        category=category,
        event_time=event_time,
    )
    parent = source_name + "/locations/" + location_id
    # Call The API.
    created_finding = client.create_finding(
        request={"parent": parent, "finding_id": finding_id, "finding": finding}
    )
    print(created_finding)
    return created_finding

如需了解 Security Command Center 中发现结果数据的存储时长,请参阅发现结果保留

更新发现结果的状态

Security Command Center 还提供了一个 API,可让您仅更新发现结果的状态。此 API 的存在只是为了提供仅更新发现结果的状态。它是一种简单 API,允许权限主账号只能修改发现结果的状态,而无权修改结果的其他方面。以下示例展示了如何将发现结果的状态更改为非活跃。

gcloud

gcloud scc findings update \
    PARENT/PARENT_ID/sources/SOURCE_ID/locations/LOCATION/findings/FINDING_NAME \
    --state=STATE

替换以下内容:

  • PARENT:相应发现所在的资源层次结构的级别;请使用 organizationsfoldersprojects
  • PARENT_ID:父级组织、文件夹或项目的数字 ID,或父级项目的字母数字 ID。
  • SOURCE_ID:发现结果的来源的数字 ID。
  • LOCATION:如果数据驻留处于启用状态,则为更新发现结果的 Security Command Center 位置;如果数据驻留处于停用状态,则使用值 global
  • FINDING_NAME:要更新的发现。
  • STATE:发现结果的状态;如果发现结果需要关注,请使用 ACTIVE;如果发现结果已得到解决,请使用 INACTIVE

Go

import (
	"context"
	"fmt"
	"io"

	securitycenter "cloud.google.com/go/securitycenter/apiv2"
	"cloud.google.com/go/securitycenter/apiv2/securitycenterpb"
)

// updateFindingState demonstrates how to update a security finding's state
// in CSCC.  findingName is the full resource name of the finding to update.
func setFindingState(w io.Writer, findingName string) error {
	// findingName := "organizations/111122222444/sources/1234/locations/global"
	// 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.SetFindingStateRequest{
		Name:  findingName,
		State: securitycenterpb.Finding_INACTIVE,
		// New state is effective immediately.
	}

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

	fmt.Fprintf(w, "Finding updated: %s\n", finding.Name)
	fmt.Fprintf(w, "Finding state: %v\n", finding.State)
	fmt.Fprintf(w, "Event time (Epoch Seconds): %d\n", finding.EventTime.Seconds)

	return nil
}

Java


import com.google.cloud.securitycenter.v2.Finding;
import com.google.cloud.securitycenter.v2.Finding.State;
import com.google.cloud.securitycenter.v2.FindingName;
import com.google.cloud.securitycenter.v2.SecurityCenterClient;
import com.google.cloud.securitycenter.v2.SetFindingStateRequest;
import java.io.IOException;

public class SetFindingsByState {

  public static void main(String[] args) throws IOException {
    // organizationId: Google Cloud Organization id.
    String organizationId = "{google-cloud-organization-id}";

    // Specify the location to list the findings.
    String location = "global";

    // The source id corresponding to the finding.
    String sourceId = "{source-id}";

    // The finding id.
    String findingId = "{finding-id}";

    setFindingState(organizationId, location, sourceId, findingId);
  }

  // Demonstrates how to update a finding's state
  public static Finding setFindingState(String organizationId, String location, String sourceId,
      String findingId) 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()) {
      // Optionally FindingName or String can be used.
      // String findingName = String.format("organizations/%s/sources/%s/locations/%s/findings/%s",
      // organizationId,sourceId,location,findingId);
      FindingName findingName = FindingName
          .ofOrganizationSourceLocationFindingName(organizationId, sourceId, location, findingId);

      SetFindingStateRequest request = SetFindingStateRequest.newBuilder()
          .setName(findingName.toString())
          .setState(State.INACTIVE)
          .build();

      // Call the API.
      Finding finding = client.setFindingState(request);

      System.out.println("Updated Finding: " + finding);
      return finding;
    }
  }
}

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';

async function createSampleFinding() {
  const uuid = require('uuid');

  const [source] = await client.createSource({
    source: {
      displayName: 'Customized Display Name V2',
      description: 'A new custom source that does X',
    },
    parent: client.organizationPath(organizationId),
  });

  const sourceId = source.name.split('/')[3];

  // Resource name of the new finding's parent. Examples:
  //  - `organizations/[organization_id]/sources/[source_id]`
  //  - `organizations/[organization_id]/sources/[source_id]/locations/[location_id]`
  const parent = `organizations/${organizationId}/sources/${sourceId}/locations/${location}`;

  // The resource this finding applied to. The Cloud Security Command Center UI can link the
  // findings for a resource to the corresponding asset of a resource if there are matches.
  const resourceName = `//cloudresourcemanager.googleapis.com/organizations/${organizationId}`;

  // Unique identifier provided by the client within the parent scope.
  // It must be alphanumeric and less than or equal to 32 characters and
  // greater than 0 characters in length.
  const findingId = uuid.v4().replace(/-/g, '');

  // Get the current timestamp.
  const eventDate = new Date();

  // Finding category.
  const category = 'MEDIUM_RISK_ONE';

  // Build the finding request object.
  const createFindingRequest = {
    parent: parent,
    findingId: findingId,
    finding: {
      resourceName,
      category,
      state: 'ACTIVE',
      // The time associated with discovering the issue.
      eventTime: {
        seconds: Math.floor(eventDate.getTime() / 1000),
        nanos: (eventDate.getTime() % 1000) * 1e6,
      },
    },
  };

  await client.createFinding(createFindingRequest);
  return {sourceId, findingId};
}

const {sourceId, findingId} = await createSampleFinding();

// The name is the full resource name of the source the finding should be associated with.
const name = `organizations/${organizationId}/sources/${sourceId}/locations/${location}/findings/${findingId}`;

// Build the request.
const eventTime = new Date();

const findingRequest = {
  name,
  state: 'INACTIVE',
  // use now as the time when the new state takes effect.
  startTime: {
    seconds: Math.floor(eventTime.getTime() / 1000),
    nanos: (eventTime.getTime() % 1000) * 1e6,
  },
};

async function setFindingState() {
  // Call the API.
  const [response] = await client.setFindingState(findingRequest);
  console.log('Set finding state: %j', response);
}

await setFindingState();

后续步骤

详细了解如何使用客户端库访问 Security Command Center