feat: Add new gcloud commands, API clients, and third-party libraries across various services.

This commit is contained in:
2026-01-01 20:26:35 +01:00
parent 5e23cbece0
commit a19e592eb7
25221 changed files with 8324611 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command group for Cloud SCC Findings."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class Finding(base.Group):
"""Manage Cloud SCC (Security Command Center) findings."""
category = base.SECURITY_CATEGORY

View File

@@ -0,0 +1,127 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command to bulk mute Security Command Center findings based on a filter."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from googlecloudsdk.api_lib.scc import securitycenter_client
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.scc import flags as scc_flags
from googlecloudsdk.command_lib.scc import util as scc_util
from googlecloudsdk.command_lib.scc.findings import util
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA, base.ReleaseTrack.ALPHA)
class BulkMute(base.Command):
"""Bulk mute Security Command Center findings based on a filter."""
detailed_help = {
"DESCRIPTION": (
"Bulk mute Security Command Center findings based on a filter."
),
"EXAMPLES": """
To bulk mute findings given organization ``123'' based on a filter on
category that equals ``XSS_SCRIPTING'', run:
$ {command} --organization=organizations/123
--filter="category=\\"XSS_SCRIPTING\\""
To bulk mute findings given folder ``123'' based on a filter on category
that equals ``XSS_SCRIPTING'', run:
$ {command} --folder=folders/123 --filter="category=\\"XSS_SCRIPTING\\""
To bulk mute findings given project ``123'' based on a filter on category
that equals ``XSS_SCRIPTING'', run:
$ {command} --project=projects/123
--filter="category=\\"XSS_SCRIPTING\\""
To bulk mute findings given organization ``123'' based on a filter on
category that equals ``XSS_SCRIPTING'' and `location=eu` run:
$ {command} --organization=organizations/123
--filter="category=\\"XSS_SCRIPTING\\"" --location=locations/eu
""",
"API REFERENCE": """
This command uses the Security Command Center API. For more information,
see [Security Command Center API.](https://cloud.google.com/security-command-center/docs/reference/rest)""",
}
@staticmethod
def Args(parser):
# Create argument group for parent, this can be org | folder | project.
parent_group = parser.add_group(mutex=True, required=True)
parent_group.add_argument(
"--organization",
help="""
Organization where the findings reside. Formatted as
``organizations/123'' or just ``123''.""",
)
parent_group.add_argument(
"--folder",
help="""
Folder where the findings reside. Formatted as ``folders/456'' or just
``456''.""",
)
parent_group.add_argument(
"--project",
help="""
Project (id or number) where the findings reside. Formatted as
``projects/789'' or just ``789''.""",
)
parser.add_argument(
"--filter",
help="Expression that identifies findings that should be muted.",
required=True,
)
# To accept both lower and uppercase arguments for the choices we use
# base.ChoiceArgument.
base.ChoiceArgument(
"--mute-state",
default="muted",
choices=["muted", "undefined"],
help_str="Desired mute state of the finding.",
).AddToParser(parser)
scc_flags.API_VERSION_FLAG.AddToParser(parser)
scc_flags.LOCATION_FLAG.AddToParser(parser)
def Run(self, args):
version = scc_util.GetVersionFromArguments(args)
# Create the request and include the filter from args.
messages = securitycenter_client.GetMessages(version)
request = messages.SecuritycenterOrganizationsFindingsBulkMuteRequest()
request.bulkMuteFindingsRequest = messages.BulkMuteFindingsRequest(
filter=args.filter,
muteState=util.ConvertMuteStateInput(args.mute_state, messages)
)
request.parent = util.ValidateAndGetParent(args)
args.filter = ""
if version == "v2":
request.parent = util.ValidateLocationAndGetRegionalizedParent(
args, request.parent
)
client = securitycenter_client.GetClient(version)
return client.organizations_findings.BulkMute(request)

View File

@@ -0,0 +1,190 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for creating a Cloud Security Command Center Finding."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from googlecloudsdk.api_lib.scc import securitycenter_client
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.scc import flags as scc_flags
from googlecloudsdk.command_lib.scc import util as scc_util
from googlecloudsdk.command_lib.scc.findings import flags
from googlecloudsdk.command_lib.scc.findings import util
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core.util import times
@base.ReleaseTracks(
base.ReleaseTrack.GA, base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA
)
@base.UniverseCompatible
class Create(base.CreateCommand):
"""Create a Security Command Center finding."""
detailed_help = {
"DESCRIPTION": "Create a Security Command Center finding.",
"EXAMPLES": f"""
Create an ACTIVE finding `testFinding` with category: XSS_SCRIPTING
attached to project with project number `9876` under organization `123456` and source
`5678`:
$ {{command}} `testFinding` --organization=123456 --source=5678
--state=ACTIVE --category='XSS_SCRIPTING'
--event-time=2023-01-11T07:00:06.861Z
--resource-name='//cloudresourcemanager.{properties.VALUES.core.universe_domain.Get()}/projects/9876'
Create an ACTIVE finding `testFinding` with category: XSS_SCRIPTING
attached to project with project number `9876` under organization `123456` and source
`5678` using the full resource name:
$ {{command}} organizations/123456/sources/5678/findings/testFinding
--state=ACTIVE --category='XSS_SCRIPTING'
--event-time=2023-01-11T07:00:06.861Z
--resource-name='//cloudresourcemanager.{properties.VALUES.core.universe_domain.Get()}/projects/9876'
Create an ACTIVE finding `testFinding` with category: `XSS_SCRIPTING`
attached to project with project number`9876` under organization `123456`, source
`5678` and `location=eu`:
$ {{command}} `testFinding` --organization=123456 --source=5678
--state=ACTIVE --category='XSS_SCRIPTING'
--event-time=2023-01-11T07:00:06.861Z
--resource-name='//cloudresourcemanager.{properties.VALUES.core.universe_domain.Get()}/projects/9876' --location=eu""",
"API REFERENCE": """
This command uses the Security Command Center API. For more information,
see [Security Command Center API.](https://cloud.google.com/security-command-center/docs/reference/rest)""",
}
@staticmethod
def Args(parser):
# Add shared flags and finding positional argument.
flags.CreateFindingArg().AddToParser(parser)
flags.EXTERNAL_URI_FLAG.AddToParser(parser)
flags.SOURCE_PROPERTIES_FLAG.AddToParser(parser)
flags.STATE_FLAG.AddToParser(parser)
flags.EVENT_TIME_FLAG_REQUIRED.AddToParser(parser)
scc_flags.API_VERSION_FLAG.AddToParser(parser)
scc_flags.LOCATION_FLAG.AddToParser(parser)
parser.add_argument(
"--category",
help="""
Taxonomy group within findings from a given source. Example:
XSS_SCRIPTING""",
required=True,
)
parser.add_argument(
"--resource-name",
help="""
Full resource name of the Google Cloud Platform resource this finding is
for.""",
required=True,
)
def Run(self, args):
version = scc_util.GetVersionFromArguments(args, args.finding)
if version == "v1":
request = _V1GenerateRequestArgumentsForCreateCommand(args)
else:
request = _V2GenerateRequestArgumentsForCreateCommand(args)
client = securitycenter_client.GetClient(version)
response = client.organizations_sources_findings.Create(request)
log.status.Print("Created.")
return response
def _V2GenerateRequestArgumentsForCreateCommand(args):
"""Generate the request's finding name, finding ID, parent and v2 googleCloudSecuritycenterV2Finding using specified arguments.
Args:
args: (argparse namespace)
Returns:
req: Modified request
"""
messages = securitycenter_client.GetMessages("v2")
request = messages.SecuritycenterOrganizationsSourcesFindingsCreateRequest()
request.googleCloudSecuritycenterV2Finding = (
messages.GoogleCloudSecuritycenterV2Finding(
category=args.category,
resourceName=args.resource_name,
state=util.ConvertStateInput(args.state, "v2"),
)
)
request.googleCloudSecuritycenterV2Finding.externalUri = args.external_uri
if args.IsKnownAndSpecified("source_properties"):
request.googleCloudSecuritycenterV2Finding.sourceProperties = (
util.ConvertSourceProperties(args.source_properties, "v2")
)
# Convert event_time argument to correct format.
event_time_dt = times.ParseDateTime(args.event_time)
request.googleCloudSecuritycenterV2Finding.eventTime = times.FormatDateTime(
event_time_dt
)
util.ValidateMutexOnFindingAndSourceAndOrganization(args)
finding_name = util.GetFullFindingName(args, "v2")
request.parent = util.GetSourceParentFromFindingName(finding_name, "v2")
request.findingId = util.GetFindingIdFromName(finding_name)
if not request.googleCloudSecuritycenterV2Finding:
request.googleCloudSecuritycenterV2Finding = (
messages.GoogleCloudSecuritycenterV2Finding()
)
request.googleCloudSecuritycenterV2Finding.name = finding_name
return request
def _V1GenerateRequestArgumentsForCreateCommand(args):
"""Generate the request's finding name, finding ID, parent and v1 Finding using specified arguments.
Args:
args: (argparse namespace)
Returns:
req: Modified request
"""
messages = securitycenter_client.GetMessages("v1")
request = messages.SecuritycenterOrganizationsSourcesFindingsCreateRequest()
request.finding = messages.Finding(
category=args.category,
resourceName=args.resource_name,
state=util.ConvertStateInput(args.state, "v1"),
)
request.finding.externalUri = args.external_uri
if args.IsKnownAndSpecified("source_properties"):
request.finding.sourceProperties = util.ConvertSourceProperties(
args.source_properties, "v1"
)
# Convert event_time argument to correct format.
event_time_dt = times.ParseDateTime(args.event_time)
request.finding.eventTime = times.FormatDateTime(event_time_dt)
util.ValidateMutexOnFindingAndSourceAndOrganization(args)
finding_name = util.GetFullFindingName(args, "v1")
request.parent = util.GetSourceParentFromFindingName(finding_name, "v1")
request.findingId = util.GetFindingIdFromName(finding_name)
if not request.finding:
request.finding = messages.Finding()
request.finding.name = finding_name
return request

View File

@@ -0,0 +1,92 @@
# -*- coding: utf-8 -*- #
# Copyright 2025 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command to export Security Command Center findings to bigquery."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from googlecloudsdk.api_lib.scc import securitycenter_client
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.scc import flags as scc_flags
from googlecloudsdk.command_lib.scc.findings import flags
from googlecloudsdk.command_lib.scc.findings import util
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.GA)
class Export(base.Command):
"""Export Security Command Center findings to bigquery."""
detailed_help = {
"DESCRIPTION": "Export Security Command Center findings to bigquery.",
"EXAMPLES": """
To export findings for a given parent ``organizations/123/sources/456/locations/global`` and dataset ``projects/project_id/datasets/dataset_id`` run:
$ {command} organizations/123 --dataset=projects/project_id/datasets/dataset_id --source=456 --location=global
""",
"API REFERENCE": (
"""
This command uses the Security Command Center API. For more information,
see [Security Command Center API.](https://cloud.google.com/security-command-center/docs/reference/rest)"""
),
}
@staticmethod
def Args(parser):
scc_flags.AppendParentArg()[0].AddToParser(parser)
flags.SOURCE_FLAG.AddToParser(parser)
scc_flags.LOCATION_FLAG.AddToParser(parser)
parser.add_argument(
"--dataset",
help="BigQuery dataset to export findings to.",
required=True,
)
def Run(self, args):
version = "v2"
messages = securitycenter_client.GetMessages(version)
client = securitycenter_client.GetClient(version)
if args.parent is None:
raise ValueError("Parent must be specified.")
request = (
messages.SecuritycenterOrganizationsSourcesLocationsFindingsExportRequest()
)
validated_dataset = util.ValidateDataset(args.dataset)
request.exportFindingsRequest = messages.ExportFindingsRequest(
bigQueryDestination=messages.BigQueryDestination(
dataset=validated_dataset
),
)
request.parent = _GenerateParentResource(args, version)
return client.organizations_sources_locations_findings.Export(request)
def _GenerateParentResource(args, version="v2"):
"""Generate a parent's name and parent using org, source and finding id."""
util.ValidateMutexOnSourceAndParent(args)
if args.parent and "/sources/" in args.parent:
args.source = args.parent
parent = util.GetFullSourceName(args, version)
return parent

View File

@@ -0,0 +1,194 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command to Filter an organization or source's findings and group them by their specified properties."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import re
import sys
from googlecloudsdk.api_lib.scc import securitycenter_client
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.scc import flags as scc_flags
from googlecloudsdk.command_lib.scc import util as scc_util
from googlecloudsdk.command_lib.scc.findings import flags
from googlecloudsdk.command_lib.scc.findings import util
from googlecloudsdk.core.util import times
@base.ReleaseTracks(
base.ReleaseTrack.GA, base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA
)
class Group(base.Command):
"""Filter an organization or source's findings and groups them by their specified properties."""
detailed_help = {
"DESCRIPTION": """
To group across all sources provide a '-' as the source id.""",
"EXAMPLES": """
Group findings under organization `123456` across all sources by their
category:
$ {command} 123456 --group-by="category"
Group findings under project `example-project` across all sources by
their category:
$ {command} projects/example-project --group-by="category"
Group findings under folders `456` across all sources by their
category:
$ {command} folders/456 --group-by="category"
Group findings under organization `123456` and source `5678`, by their
category:
$ {command} 123456 --source=5678 --group-by="category"
Group ACTIVE findings under organization `123456` and source `5678`,
by their category:
$ {command} 123456 --source=5678 --group-by="category"
--filter="state=\\"ACTIVE\\""
Group findings under organization `123456` and `location=eu` across
all sources by their category:
$ {command} 123456 --group-by="category" --location=eu""",
"API REFERENCE": """
This command uses the Security Command Center API. For more information,
see [Security Command Center API.](https://cloud.google.com/security-command-center/docs/reference/rest)""",
}
@staticmethod
def Args(parser):
# Add shared flags and parent positional argument
scc_flags.AppendParentArg()[0].AddToParser(parser)
scc_flags.PAGE_TOKEN_FLAG.AddToParser(parser)
scc_flags.READ_TIME_FLAG.AddToParser(parser)
flags.COMPARE_DURATION_FLAG.AddToParser(parser)
flags.SOURCE_FLAG.AddToParser(parser)
scc_flags.API_VERSION_FLAG.AddToParser(parser)
scc_flags.LOCATION_FLAG.AddToParser(parser)
parser.add_argument(
"--filter",
help="""
Expression that defines the filter to apply across findings. The
expression is a list of one or more restrictions combined via logical
operators 'AND' and 'OR'. Parentheses are supported, and 'OR' has higher
precedence than 'AND'. Restrictions have the form
'<field> <operator> <value>' and may have a '-' character in front of
them to indicate negation. Examples include: name,
source_properties.a_property, security_marks.marks.marka.
The supported operators are:
* '=' for all value types.
* '>', '<', '>=', '<=' for integer values.
* ':', meaning substring matching, for strings.
The supported value types are:string literals in quotes, integer
literals without quotes, boolean literals 'true' and 'false' without
quotes. Some example filters: 'source_properties.size = 100',
'category=\\"XSS\\" AND event_time > 10' etc.""",
)
parser.add_argument(
"--group-by",
help="""
Expression that defines what findings fields to use for grouping
(including 'state'). String value should follow SQL syntax: comma
separated list of fields. For example: "parent,resource_name".
The following fields are supported:
* resource_name
* category
* state
* parent""",
)
parser.add_argument(
"--page-size",
type=arg_parsers.BoundedInt(1, sys.maxsize, unlimited=True),
help="""
Maximum number of results to return in a single response. Default is 10,
minimum is 1, maximum is 1000.""",
)
def Run(self, args):
deprecated_args = ["compare_duration", "read_time"]
version = util.GetApiVersionUsingDeprecatedArgs(args, deprecated_args)
messages = securitycenter_client.GetMessages(version)
request = messages.SecuritycenterOrganizationsSourcesFindingsGroupRequest()
request.groupFindingsRequest = messages.GroupFindingsRequest()
# Populate request fields from args.
if version == "v1":
# Include args deprecated in v2.
if args.IsKnownAndSpecified("compare_duration"):
# Process compareDuration argument.
request.groupFindingsRequest.compareDuration = args.compare_duration
compare_duration_iso = times.ParseDuration(
request.groupFindingsRequest.compareDuration
)
request.groupFindingsRequest.compareDuration = (
times.FormatDurationForJson(compare_duration_iso)
)
if args.IsKnownAndSpecified("read_time"):
# Get DateTime from string and convert to the format required by API.
request.groupFindingsRequest.readTime = args.read_time
read_time_dt = times.ParseDateTime(
request.groupFindingsRequest.readTime
)
request.groupFindingsRequest.readTime = times.FormatDateTime(
read_time_dt
)
request.groupFindingsRequest.filter = args.filter
request.groupFindingsRequest.groupBy = args.group_by
request.groupFindingsRequest.pageSize = args.page_size
request.groupFindingsRequest.pageToken = args.page_token
request = _GenerateParentForGroupCommand(args, request, version)
client = securitycenter_client.GetClient(version)
result = client.organizations_sources_findings.Group(request)
return result
def _GenerateParentForGroupCommand(args, req, version="v1"):
"""Generate a finding's name and parent using org, source and finding id."""
util.ValidateMutexOnSourceAndParent(args)
req.groupFindingsRequest.filter = args.filter
args.filter = ""
region_resource_patern = re.compile(
"(organizations|projects|folders)/[a-z0-9]+/sources/[0-9-]{0,62}/locations/[A-Za-z0-9-]{0,62}$"
)
parent = scc_util.GetParentFromPositionalArguments(args)
if region_resource_patern.match(parent):
req.parent = parent
return req
resource_pattern = re.compile(
"(organizations|projects|folders)/[a-z0-9]+/sources/[0-9-]{0,62}$"
)
if resource_pattern.match(parent):
args.source = parent
req.parent = util.GetFullSourceName(args, version)
return req

View File

@@ -0,0 +1,205 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for listing an organization or source's findings."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from apitools.base.py import list_pager
from googlecloudsdk.api_lib.scc import securitycenter_client
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.scc import flags as scc_flags
from googlecloudsdk.command_lib.scc import util as scc_util
from googlecloudsdk.command_lib.scc.findings import flags
from googlecloudsdk.command_lib.scc.findings import util
from googlecloudsdk.core import properties
from googlecloudsdk.core.util import times
# base.ListCommand defines --filter, --flatten, --limit, --page-size, --sort-by
# and --uri flags
@base.ReleaseTracks(
base.ReleaseTrack.GA, base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA
)
@base.DefaultUniverseOnly
class List(base.ListCommand):
"""List an organization or source's findings."""
detailed_help = {
"DESCRIPTION": """
List an organization or source's findings. To list across all
sources provide a '-' as the source id.""",
"EXAMPLES": (
f"""
List all ACTIVE findings under organization `123456` across all
sources:
$ {{command}} 123456 --filter="state=\\"ACTIVE\\""
List all ACTIVE findings under project `abc` across all sources:
$ {{command}} projects/abc --filter="state=\\"ACTIVE\\""
List all ACTIVE findings under folder `456` across all sources:
$ {{command}} folders/456 --filter="state=\\"ACTIVE\\""
List all ACTIVE findings under organization `123456` and source
`5678`:
$ {{command}} 123456 --source=5678 --filter="state=\\"ACTIVE\\""
Only list category and resource_name of all ACTIVE findings under
organization `123456` and source `5678`:
$ {{command}} 123456 --source=5678 --filter="state=\\"ACTIVE\\""
--field-mask="finding.category,finding.resource_name"
List all ACTIVE findings of XSS category/type, under organization
`123456` and source `5678`:
$ {{command}} 123456 --source=5678
--filter="state=\\"ACTIVE\\" AND category=\\"XSS\\""
List all findings attached to a particular resource under organization
`123456`:
$ {{command}} 123456
--filter="resource_name=\\"//container.{properties.VALUES.core.universe_domain.Get()}/projects/pid/zones/zone-id/clusters/cluster-id\\""
List all ACTIVE findings that took place on `2019-01-01T01:00:00 GMT`
time, under organization `123456`:
$ {{command}} 123456
--filter="state=\\"ACTIVE\\" AND event_time > 1546304400000""
List all findings under organization `123456` across all sources and
`location=eu`:
$ {{command}} 123456 --location=eu"""
),
"API REFERENCE": """
This command uses the Security Command Center API. For more information,
see [Security Command Center API.](https://cloud.google.com/security-command-center/docs/reference/rest)""",
}
@staticmethod
def Args(parser):
# Remove URI flag.
base.URI_FLAG.RemoveFromParser(parser)
# Add shared flags and parent positional argument.
scc_flags.AppendParentArg()[0].AddToParser(parser)
flags.AddParentGroup(parser)
scc_flags.PAGE_TOKEN_FLAG.AddToParser(parser)
scc_flags.READ_TIME_FLAG.AddToParser(parser)
flags.COMPARE_DURATION_FLAG.AddToParser(parser)
flags.SOURCE_FLAG.AddToParser(parser)
scc_flags.API_VERSION_FLAG.AddToParser(parser)
scc_flags.LOCATION_FLAG.AddToParser(parser)
parser.add_argument(
"--field-mask",
help="""
Field mask to specify the finding fields listed in the response. An
empty field mask will list all fields. For example:
--field-mask="finding.category,finding.resource_name" will only output
category and resource_name for the findings in addition to default
attributes. Notice the difference between hyphens (-) used with flags
v/s camel case used in field masks. An empty or missing field mask will
list all fields.""",
)
# Cloud SCC doesn't use gcloud's sort-by flag since that sorts at the client
# level while Cloud SCC's ordering needs to be passed to the server.
parser.add_argument(
"--order-by",
help="""
Expression that defines what fields and order to use for sorting.
String value should follow SQL syntax: comma separated list of fields.
For example: "name,resource_properties.a_property". The default sorting
order is ascending. To specify descending order for a field, a suffix "
desc" should be appended to the field name. For example:
--order-by="name desc,source_properties.a_property" will order by name
in descending order while source_properties.a_property in ascending
order.""",
)
def Run(self, args):
deprecated_args = ["compare_duration", "read_time"]
version = util.GetApiVersionUsingDeprecatedArgs(args, deprecated_args)
messages = securitycenter_client.GetMessages(version)
request = messages.SecuritycenterOrganizationsSourcesFindingsListRequest()
# Populate request fields from args.
if version == "v1":
# Include args deprecated in v2.
if args.IsKnownAndSpecified("compare_duration"):
# Process compareDuration argument.
request.compareDuration = args.compare_duration
compare_duration_iso = times.ParseDuration(request.compareDuration)
request.compareDuration = times.FormatDurationForJson(
compare_duration_iso
)
if args.IsKnownAndSpecified("read_time"):
# Get DateTime from string and convert to the format required by API.
request.readTime = args.read_time
read_time_dt = times.ParseDateTime(request.readTime)
request.readTime = times.FormatDateTime(read_time_dt)
request.fieldMask = args.field_mask
if request.fieldMask is not None:
request.fieldMask = scc_util.CleanUpUserMaskInput(request.fieldMask)
request.filter = args.filter
args.filter = ""
request.orderBy = args.order_by
request.pageSize = args.page_size
request.pageToken = args.page_token
request.parent = args.parent
request = _GenerateParent(args, request, version)
client = securitycenter_client.GetClient(version)
# Automatically handle pagination. All findings are returned regarldess of
# --page-size argument.
return list_pager.YieldFromList(
client.organizations_sources_findings,
request,
batch_size_attribute="pageSize",
batch_size=args.page_size,
field="listFindingsResults",
)
def _GenerateParent(args, req, version="v1"):
"""Generates a finding's parent using org and source and hook up filter.
Args:
args: (argparse namespace)
req: request
version: API version for the request
Returns:
req: Modified request
"""
util.ValidateMutexOnSourceAndParent(args)
if args.parent and "/sources/" in args.parent:
args.source = args.parent
req.parent = util.GetFullSourceName(args, version)
return req

View File

@@ -0,0 +1,135 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for listing a finding's security marks."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from googlecloudsdk.api_lib.scc import securitycenter_client
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.scc import flags as scc_flags
from googlecloudsdk.command_lib.scc import util as scc_util
from googlecloudsdk.command_lib.scc.findings import flags
from googlecloudsdk.command_lib.scc.findings import util
from googlecloudsdk.core.util import times
@base.ReleaseTracks(
base.ReleaseTrack.GA, base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA
)
@base.DefaultUniverseOnly
class ListMarks(base.ListCommand):
"""List a finding's security marks."""
detailed_help = {
"DESCRIPTION": "List a finding's security marks.",
"EXAMPLES": """
List all security marks for `testFinding` under organization `123456` and
source `5678`:
$ {command} `testFinding` --organization=123456 --source=5678
List all security marks for `testFinding` under project `example-project`
and source `5678`:
$ {command} projects/example-project/sources/5678/findings/testFinding
List all security marks for `testFinding` under folder `456` and source
`5678`:
$ {command} folders/456/sources/5678/findings/testFinding
List all security marks for `testFinding` under organization `123456`,
source `5678` and `location=eu`:
$ {command} `testFinding` --organization=123456 --source=5678
--location=eu""",
"API REFERENCE": """
This command uses the Security Command Center API. For more information,
see [Security Command Center API.](https://cloud.google.com/security-command-center/docs/reference/rest)""",
}
@staticmethod
def Args(parser):
# Remove URI flag.
base.URI_FLAG.RemoveFromParser(parser)
# Add shared flags and finding positional argument
flags.FINDING_FLAG.AddToParser(parser)
flags.SOURCE_FLAG.AddToParser(parser)
scc_flags.PAGE_TOKEN_FLAG.AddToParser(parser)
scc_flags.READ_TIME_FLAG.AddToParser(parser)
scc_flags.API_VERSION_FLAG.AddToParser(parser)
scc_flags.LOCATION_FLAG.AddToParser(parser)
group = parser.add_mutually_exclusive_group(required=False)
group.add_argument(
"--organization",
help="The organization ID (e.g., 123) that contains the finding.",
)
group.add_argument(
"--folder",
help="The folder ID (e.g., 456) that contains the finding.",
)
group.add_argument(
"--project",
help=(
"The project ID (e.g., example-project) that contains the finding."
),
)
def Run(self, args):
# Determine what version to call from --location and --api-version.
version = _GetApiVersion(args)
messages = securitycenter_client.GetMessages(version)
request = messages.SecuritycenterOrganizationsSourcesFindingsListRequest()
# Populate the request fields from args
request.pageToken = args.page_token
if version == "v1" and args.IsKnownAndSpecified("read_time"):
request.readTime = args.read_time
# Get DateTime from string and convert to the format required by API.
read_time_dt = times.ParseDateTime(request.readTime)
request.readTime = times.FormatDateTime(read_time_dt)
request = _GenerateParent(args, request, version)
client = securitycenter_client.GetClient(version)
list_findings_response = client.organizations_sources_findings.List(request)
# Get the security marks
response = util.ExtractSecurityMarksFromResponse(
list_findings_response.listFindingsResults, args
)
return response
def _GenerateParent(args, req, version):
"""Generates a finding's parent and adds filter based on finding name."""
util.ValidateMutexOnFindingAndSourceAndOrganization(args)
finding_name = util.GetFullFindingName(args, version)
req.parent = util.GetSourceParentFromFindingName(finding_name, version)
req.filter = f"name : \"{util.GetFindingIdFromName(finding_name)}\""
return req
def _GetApiVersion(args):
"""Determine what version to call from --location and --api-version."""
deprecated_args = ["compare_duration", "read_time"]
return scc_util.GetVersionFromArguments(
args, args.finding, deprecated_args
)

View File

@@ -0,0 +1,154 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command to Update a Cloud Security Command Center finding's mute state."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from googlecloudsdk.api_lib.scc import securitycenter_client
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.scc import flags as scc_flags
from googlecloudsdk.command_lib.scc import util as scc_util
from googlecloudsdk.command_lib.scc.findings import util
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA, base.ReleaseTrack.ALPHA)
class SetMute(base.Command):
"""Update a Security Command Center finding's mute state."""
detailed_help = {
"DESCRIPTION": (
"Update a Security Command Center finding's mute state."
),
"EXAMPLES": """
To update finding's mute state to ``MUTED'', given finding
`organizations/123/sources/456/findings/789`, run:
$ {command} 789 --organization=organizations/123 --source=456
--mute=MUTED
To update finding's mute state to ``UNMUTED'', given finding
`organizations/123/sources/456/findings/789`, run:
$ {command} 789 --organization=organizations/123 --source=456
--mute=UNMUTED
To update finding's mute state to ``MUTED'', given finding
`folders/123/sources/456/findings/789`, run:
$ {command} 789 --folder=folders/123 --source=456 --mute=MUTED
To update finding's mute state to ``MUTED'', given finding
`projects/123/sources/456/findings/789`, run:
$ {command} 789 --project=projects/123 --source=456 --mute=MUTED
To update finding's mute state to ``MUTED'', given finding
`organizations/123/sources/456/findings/789` and `location=eu`, run:
$ {command} 789 --organization=organizations/123 --source=456
--mute=MUTED --location=locations/eu""",
"API REFERENCE": """
This command uses the Security Command Center API. For more information,
see [Security Command Center API.](https://cloud.google.com/security-command-center/docs/reference/rest)""",
}
@staticmethod
def Args(parser):
# Add flags and finding positional argument.
scc_flags.API_VERSION_FLAG.AddToParser(parser)
scc_flags.LOCATION_FLAG.AddToParser(parser)
parser.add_argument(
"finding",
help="ID of the finding or the full resource name of the finding.",
)
source_group = parser.add_group(mutex=True)
source_group.add_argument(
"--organization",
help="""Organization where the finding resides. Formatted as
``organizations/123'' or just ``123''.""",
)
source_group.add_argument(
"--folder",
help="""Folder where the finding resides. Formatted as ``folders/456''
or just ``456''.""",
)
source_group.add_argument(
"--project",
help="""Project (id or number) where the finding resides. Formatted as
``projects/789'' or just ``789''.""",
)
# To accept both lower and uppercase arguments for the choices we use
# base.ChoiceArgument.
base.ChoiceArgument(
"--mute",
required=True,
default="mute_unspecified",
choices=["muted", "unmuted", "undefined"],
help_str="Desired mute state of the finding.",
).AddToParser(parser)
parser.add_argument("--source", help="ID of the source.")
def Run(self, args):
version = scc_util.GetVersionFromArguments(args, args.finding)
messages = securitycenter_client.GetMessages(version)
# Create and build the request.
request = (
messages.SecuritycenterOrganizationsSourcesFindingsSetMuteRequest()
)
request.setMuteRequest = messages.SetMuteRequest()
set_mute_dict = {
"mute_unspecified": (
messages.SetMuteRequest.MuteValueValuesEnum.MUTE_UNSPECIFIED
),
"muted": messages.SetMuteRequest.MuteValueValuesEnum.MUTED,
"unmuted": messages.SetMuteRequest.MuteValueValuesEnum.UNMUTED,
"undefined": messages.SetMuteRequest.MuteValueValuesEnum.UNDEFINED,
}
# The muted option has to be case insensitive, so we convert to lower before
# mapping to set_mute_dict.
args.mute = args.mute.lower()
request.setMuteRequest.mute = set_mute_dict.get(
args.mute, messages.SetMuteRequest.MuteValueValuesEnum.UNDEFINED
)
# Set the finding's Mute State.
parent = util.ValidateAndGetParent(args)
if parent is not None:
util.ValidateSourceAndFindingIdIfParentProvided(args)
if version == "v1":
request.name = (
parent + "/sources/" + args.source + "/findings/" + args.finding
)
elif version == "v2":
source_parent = parent + "/sources/" + args.source
regionalized_parent = util.ValidateLocationAndGetRegionalizedParent(
args, source_parent
)
request.name = regionalized_parent + "/findings/" + args.finding
else:
request.name = util.GetFullFindingName(args, version)
# Make the request to the API.
client = securitycenter_client.GetClient(version)
response = client.organizations_sources_findings.SetMute(request)
return response

View File

@@ -0,0 +1,206 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command to Update a Cloud Security Command Center finding."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import datetime
from googlecloudsdk.api_lib.scc import securitycenter_client
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.scc import flags as scc_flags
from googlecloudsdk.command_lib.scc import util as scc_util
from googlecloudsdk.command_lib.scc.findings import flags
from googlecloudsdk.command_lib.scc.findings import util
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core.util import times
@base.ReleaseTracks(
base.ReleaseTrack.GA, base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA
)
@base.DefaultUniverseOnly
class Update(base.UpdateCommand):
"""Update a Security Command Center finding."""
detailed_help = {
"DESCRIPTION": "Update a Security Command Center finding.",
"EXAMPLES": """
Update testFinding's state from `ACTIVE` to `INACTIVE`:
$ {command} `testFinding` --organization=123456 --source=5678
--state=INACTIVE
Update testFinding's state from `ACTIVE` to `INACTIVE` using project name
for example-project:
$ {command} projects/example-project/sources/5678/findings/testFinding
--state=INACTIVE
Update testFinding's state from `ACTIVE` to `INACTIVE` using folder name
`456`:
$ {command} folders/456/sources/5678/findings/testFinding
--state=INACTIVE
Override all source properties on `testFinding`:
$ {command} `testFinding` --organization=123456 --source=5678
--source-properties="propKey1=propVal1,propKey2=propVal2"
Selectively update a specific source property on `testFinding`:
$ {command} `testFinding` --organization=123456 --source=5678
--source-properties="propKey1=propVal1,propKey2=propVal2" --update-mask="source_properties.propKey1"
Update finding `testFinding` with `location=eu`, state from `ACTIVE` to
`INACTIVE`:
$ {command} `testFinding` --organization=123456 --source=5678
--state=INACTIVE --location=eu""",
"API REFERENCE": """
This command uses the Security Command Center API. For more information,
see [Security Command Center API.](https://cloud.google.com/security-command-center/docs/reference/rest)""",
}
@staticmethod
def Args(parser):
# Add flags and finding positional argument.
flags.FINDING_FLAG.AddToParser(parser)
flags.SOURCE_FLAG.AddToParser(parser)
flags.AddParentGroup(parser)
flags.EVENT_TIME_FLAG_NOT_REQUIRED.AddToParser(parser)
flags.EXTERNAL_URI_FLAG.AddToParser(parser)
flags.SOURCE_PROPERTIES_FLAG.AddToParser(parser)
flags.STATE_FLAG.AddToParser(parser)
scc_flags.API_VERSION_FLAG.AddToParser(parser)
scc_flags.LOCATION_FLAG.AddToParser(parser)
parser.add_argument(
"--update-mask",
help="""
Optional: If left unspecified (default), an update-mask is automatically
created using the flags specified in the command and only those values
are updated. For example: --external-uri='<some-uri>'
--event-time='<some-time>' would automatically generate
--update-mask='external_uri,event_time'. Note that as a result, only
external-uri and event-time are updated for the given finding and
everything else remains untouched. If you want to delete
attributes/properties (that are not being changed in the update command)
use an empty update-mask (''). That will delete all the mutable
properties/attributes that aren't specified as flags in the update
command. In the above example it would delete source-properties.
State can be toggled from ACTIVE to INACTIVE and vice-versa but it
cannot be deleted.""",
)
parser.display_info.AddFormat(properties.VALUES.core.default_format.Get())
def Run(self, args):
# Determine what version to call from --location and --api-version.
version = scc_util.GetVersionFromArguments(args, args.finding)
messages = securitycenter_client.GetMessages(version)
request = messages.SecuritycenterOrganizationsSourcesFindingsPatchRequest()
request.updateMask = args.update_mask
# Create update mask if none was specified. The API supports both snake
# and camel casing. To pass the existing tests we map the arguments from
# kebab casing to camel casing in the 'mutable_fields' dict.
if not args.update_mask:
mutable_fields = {
"--event-time": "eventTime",
"--external-uri": "externalUri",
"--source-properties": "sourceProperties",
"--state": "state",
}
request.updateMask = ""
for arg in args.GetSpecifiedArgNames():
if arg in mutable_fields:
request.updateMask += "," + mutable_fields[arg]
# Delete the first character of the update mask if it is a comma.
if request.updateMask.startswith(","):
request.updateMask = request.updateMask[1:]
if version == "v1":
request.finding = messages.Finding()
request.finding.externalUri = args.external_uri
if args.source_properties:
request.finding.sourceProperties = util.ConvertSourceProperties(
args.source_properties, version
)
request.finding.state = util.ConvertStateInput(args.state, version)
else:
request.googleCloudSecuritycenterV2Finding = (
messages.GoogleCloudSecuritycenterV2Finding()
)
request.googleCloudSecuritycenterV2Finding.externalUri = args.external_uri
if args.source_properties:
request.googleCloudSecuritycenterV2Finding.sourceProperties = (
util.ConvertSourceProperties(args.source_properties, version)
)
request.googleCloudSecuritycenterV2Finding.state = util.ConvertStateInput(
args.state, version
)
request.name = _GenerateFindingName(args, version)
request.updateMask = scc_util.CleanUpUserMaskInput(request.updateMask)
request = _UpdateEventTime(args, request, version)
client = securitycenter_client.GetClient(version)
response = client.organizations_sources_findings.Patch(request)
log.status.Print("Updated.")
return response
def _GenerateFindingName(args, version):
"""Generate a finding's name using org, source and finding id."""
util.ValidateMutexOnFindingAndSourceAndOrganization(args)
return util.GetFullFindingName(args, version)
def _UpdateEventTime(args, req, version):
"""Process and include the event time of a finding."""
# Convert event_time argument to correct format.
if args.event_time:
event_time_dt = times.ParseDateTime(args.event_time)
if version == "v1":
req.finding.eventTime = times.FormatDateTime(event_time_dt)
else:
req.googleCloudSecuritycenterV2Finding.eventTime = times.FormatDateTime(
event_time_dt
)
# All requests require an event time.
if args.event_time is None:
# Get current utc time and convert to a string representation.
event_time = datetime.datetime.now(tz=datetime.timezone.utc).strftime(
"%Y-%m-%dT%H:%M:%S.%fZ"
)
if version == "v1":
if req.finding is None:
req.finding = securitycenter_client.GetMessages().Finding()
req.finding.eventTime = event_time
else:
if req.googleCloudSecuritycenterV2Finding is None:
req.googleCloudSecuritycenterV2Finding = (
securitycenter_client.GetMessages().GoogleCloudSecuritycenterV2Finding()
)
req.googleCloudSecuritycenterV2Finding.eventTime = event_time
req.updateMask = req.updateMask + ",event_time"
return req

View File

@@ -0,0 +1,166 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for Updating Cloud Security Command Center finding's security marks."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from apitools.base.py import encoding
from googlecloudsdk.api_lib.scc import securitycenter_client
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.scc import flags as scc_flags
from googlecloudsdk.command_lib.scc import util as scc_util
from googlecloudsdk.command_lib.scc.findings import flags
from googlecloudsdk.command_lib.scc.findings import util
from googlecloudsdk.core import properties
from googlecloudsdk.core.util import times
@base.ReleaseTracks(
base.ReleaseTrack.GA, base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA
)
@base.DefaultUniverseOnly
class UpdateMarks(base.UpdateCommand):
"""Update Security Command Center finding's security marks."""
detailed_help = {
"DESCRIPTION": (
"""Update Security Command Center finding's security marks."""
),
"EXAMPLES": """
Selectively update security mark `Key1` with value `v1` on testFinding. Note
that other security marks on `testFinding` are untouched:
$ {command} `testFinding` --organization=123456 --source=5678
--security-marks="key1=v1" --update-mask="marks.markKey1"
Update all security marks on `testFinding`, under project `example-project`
and source `5678`:
$ {command} projects/example-project/sources/5678/findings/testFinding
--security-marks="key1=v1,key2=v2"
Update all security marks on `testFinding`, under folder `456` and source
`5678`:
$ {command} folders/456/sources/5678/findings/testFinding
--security-marks="key1=v1,key2=v2"
Update all security marks on `testFinding`, under organization `123456` and
source `5678`:
$ {command} `testFinding` --organization=123456 --source=5678
--security-marks="key1=v1,key2=v2"
Delete all security marks on `testFinding`:
$ {command} `testFinding` --organization=123456 --source=5678
--security-marks=""
Update all security marks on `testFinding`, under project `example-project`,
source `5678` and `location=eu`:
$ {command} projects/example-project/sources/5678/findings/testFinding
--security-marks="key1=v1,key2=v2" --location=eu""",
"API REFERENCE": """
This command uses the Security Command Center API. For more information,
see [Security Command Center API.](https://cloud.google.com/security-command-center/docs/reference/rest)""",
}
@staticmethod
def Args(parser):
# Add the shared flags and positional argument.
flags.FINDING_FLAG.AddToParser(parser)
flags.SOURCE_FLAG.AddToParser(parser)
flags.AddParentGroup(parser)
scc_flags.API_VERSION_FLAG.AddToParser(parser)
scc_flags.LOCATION_FLAG.AddToParser(parser)
base.Argument(
"--security-marks",
help="""
SecurityMarks resource to be passed as the request body. It's a
key=value pair separated by comma (,). For example:
--security-marks="key1=val1,key2=val2".""",
type=arg_parsers.ArgDict(),
metavar="KEY=VALUE",
).AddToParser(parser)
parser.add_argument(
"--start-time",
help="""
Time at which the updated SecurityMarks take effect. See `$ gcloud topic
datetimes` for information on supported time formats.""",
)
parser.add_argument(
"--update-mask",
help="""
Use update-mask if you want to selectively update marks represented by
--security-marks flag. For example:
--update-mask="marks.key1,marks.key2". If you want to override all the
marks for the given finding either skip the update-mask flag or provide
an empty value (--update-mask '') for it.""",
)
parser.display_info.AddFormat(properties.VALUES.core.default_format.Get())
def Run(self, args):
# Determine what version to call from --location and --api-version.
version = _GetApiVersion(args)
messages = securitycenter_client.GetMessages(version)
request = (
messages.SecuritycenterOrganizationsSourcesFindingsUpdateSecurityMarksRequest()
)
# Convert start time to correct format
if args.start_time:
start_time_dt = times.ParseDateTime(args.start_time)
request.startTime = times.FormatDateTime(start_time_dt)
client = securitycenter_client.GetClient(version)
request.updateMask = args.update_mask
if version == "v1":
security_marks = messages.SecurityMarks()
security_marks.marks = encoding.DictToMessage(
args.security_marks, messages.SecurityMarks.MarksValue
)
request.securityMarks = security_marks
elif version == "v2":
security_marks = messages.GoogleCloudSecuritycenterV2SecurityMarks()
security_marks.marks = encoding.DictToMessage(
args.security_marks,
messages.GoogleCloudSecuritycenterV2SecurityMarks.MarksValue,
)
request.googleCloudSecuritycenterV2SecurityMarks = security_marks
request = _ValidateParentAndUpdateName(args, request, version)
if request.updateMask is not None:
request.updateMask = scc_util.CleanUpUserMaskInput(request.updateMask)
marks = client.organizations_sources_findings.UpdateSecurityMarks(request)
return marks
def _ValidateParentAndUpdateName(args, req, version):
"""Generate a security mark's name using org, source and finding id."""
util.ValidateMutexOnFindingAndSourceAndOrganization(args)
req.name = util.GetFullFindingName(args, version) + "/securityMarks"
return req
def _GetApiVersion(args):
"""Determine what version to call from --location and --api-version."""
deprecated_args = ["start_time"]
return scc_util.GetVersionFromArguments(args, args.finding, deprecated_args)