From c77ec29c992e3f86d8f0e107150fc5f063b4f607 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Fri, 10 Jul 2020 18:09:47 +0000 Subject: [PATCH 1/9] chore(samples): move samples and use standard templates for testing --- noxfile.py | 117 ++-- samples/AUTHORING_GUIDE.md | 1 + samples/CONTRIBUTING.md | 1 + samples/snippets/noxfile.py | 224 +++++++ samples/snippets/noxfile_config.py | 41 ++ samples/snippets/requirements-test.txt | 1 + samples/snippets/requirements.txt | 2 + samples/snippets/snippets_findings.py | 602 ++++++++++++++++++ samples/snippets/snippets_list_assets.py | 209 ++++++ .../snippets/snippets_notification_configs.py | 138 ++++ .../snippets_notification_receiver.py | 58 ++ .../snippets/snippets_notification_test.py | 146 +++++ samples/snippets/snippets_orgs.py | 66 ++ samples/snippets/snippets_security_marks.py | 272 ++++++++ synth.metadata | 17 +- synth.py | 7 +- 16 files changed, 1816 insertions(+), 86 deletions(-) create mode 100644 samples/AUTHORING_GUIDE.md create mode 100644 samples/CONTRIBUTING.md create mode 100644 samples/snippets/noxfile.py create mode 100644 samples/snippets/noxfile_config.py create mode 100644 samples/snippets/requirements-test.txt create mode 100644 samples/snippets/requirements.txt create mode 100644 samples/snippets/snippets_findings.py create mode 100644 samples/snippets/snippets_list_assets.py create mode 100644 samples/snippets/snippets_notification_configs.py create mode 100644 samples/snippets/snippets_notification_receiver.py create mode 100644 samples/snippets/snippets_notification_test.py create mode 100644 samples/snippets/snippets_orgs.py create mode 100644 samples/snippets/snippets_security_marks.py diff --git a/noxfile.py b/noxfile.py index dd4f09ef..758afabf 100644 --- a/noxfile.py +++ b/noxfile.py @@ -14,28 +14,33 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Generated by synthtool. DO NOT EDIT! + from __future__ import absolute_import import os -import shutil +import shutil import nox +BLACK_VERSION = "black==19.10b0" +BLACK_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"] + +DEFAULT_PYTHON_VERSION = "3.8" +SYSTEM_TEST_PYTHON_VERSIONS = ["2.7", "3.8"] +UNIT_TEST_PYTHON_VERSIONS = ["2.7", "3.5", "3.6", "3.7", "3.8"] + -@nox.session(python="3.7") +@nox.session(python=DEFAULT_PYTHON_VERSION) def lint(session): """Run linters. Returns a failure if the linters find linting errors or sufficiently serious code quality issues. """ - session.install("flake8", "black") + session.install("flake8", BLACK_VERSION) session.run( - "black", - "--check", - "google", - "tests", - "docs", + "black", "--check", *BLACK_PATHS, ) session.run("flake8", "google", "tests") @@ -45,21 +50,18 @@ def blacken(session): """Run black. Format code to uniform standard. - + This currently uses Python 3.6 due to the automated Kokoro run of synthtool. That run uses an image that doesn't have 3.6 installed. Before updating this check the state of the `gcp_ubuntu_config` we use for that Kokoro run. """ - session.install("black") + session.install(BLACK_VERSION) session.run( - "black", - "google", - "tests", - "docs", + "black", *BLACK_PATHS, ) -@nox.session(python="3.7") +@nox.session(python=DEFAULT_PYTHON_VERSION) def lint_setup_py(session): """Verify that setup.py is valid (including RST check).""" session.install("docutils", "pygments") @@ -75,6 +77,7 @@ def default(session): session.run( "py.test", "--quiet", + "--cov=google.cloud.securitycenter", "--cov=google.cloud", "--cov=tests.unit", "--cov-append", @@ -86,13 +89,13 @@ def default(session): ) -@nox.session(python=["2.7", "3.5", "3.6", "3.7", "3.8"]) +@nox.session(python=UNIT_TEST_PYTHON_VERSIONS) def unit(session): """Run the unit test suite.""" default(session) -@nox.session(python=["2.7", "3.7"]) +@nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS) def system(session): """Run the system test suite.""" system_test_path = os.path.join("tests", "system.py") @@ -112,7 +115,9 @@ def system(session): # Install all test dependencies, then install this package into the # virtualenv's dist-packages. - session.install("mock", "pytest", "google-cloud-testutils") + session.install( + "mock", "pytest", "google-cloud-testutils", + ) session.install("-e", ".") # Run py.test against the system tests. @@ -122,7 +127,7 @@ def system(session): session.run("py.test", "--quiet", system_test_folder_path, *session.posargs) -@nox.session(python="3.7") +@nox.session(python=DEFAULT_PYTHON_VERSION) def cover(session): """Run the final coverage report. @@ -135,69 +140,23 @@ def cover(session): session.run("coverage", "erase") -@nox.session(python=["2.7", "3.5", "3.6", "3.7"]) -def snippets(session): - """Run the documentation example snippets.""" - # Sanity check: Only run snippets system tests if the environment variable - # is set. - if not os.environ.get('GOOGLE_APPLICATION_CREDENTIALS', ''): - session.skip('Credentials must be set via environment variable.') - if not os.environ.get('GCLOUD_ORGANIZATION', ''): - if 'KOKORO_GFILE_DIR' in os.environ: - session.env['GCLOUD_ORGANIZATION'] = '1081635000895' - else: - session.skip('Credentials must be set via environment variable.') - if not os.environ.get('GCLOUD_PROJECT', ''): - if 'KOKORO_GFILE_DIR' in os.environ: - session.env['GCLOUD_PROJECT'] = 'project-a-id' - else: - session.skip('Credentials must be set via environment variable.') - if not os.environ.get('GCLOUD_PUBSUB_TOPIC', ''): - if 'KOKORO_GFILE_DIR' in os.environ: - session.env['GCLOUD_PUBSUB_TOPIC'] = 'projects/project-a-id/topics/notifications-sample-topic' - else: - session.skip('Credentials must be set via environment variable.') - if not os.environ.get('GCLOUD_PUBSUB_SUBSCRIPTION', ''): - if 'KOKORO_GFILE_DIR' in os.environ: - session.env['GCLOUD_PUBSUB_SUBSCRIPTION'] = 'notification_sample_subscription' - else: - session.skip('Credentials must be set via environment variable.') - - - # Install all test dependencies, then install local packages in place. - session.install('mock', 'pytest') - session.install("-r", "docs/requirements.txt") - session.install('-e', '.') - session.run( - 'py.test', - '--quiet', - os.path.join('docs', 'snippets_list_assets.py'), - os.path.join('docs', 'snippets_security_marks.py'), - os.path.join('docs', 'snippets_orgs.py'), - os.path.join('docs', 'snippets_findings.py'), - os.path.join('docs', 'snippets_security_marks.py'), - os.path.join('docs', 'snippets_notification_test.py'), - - - *session.posargs - ) - - -@nox.session(python="3.7") +@nox.session(python=DEFAULT_PYTHON_VERSION) def docs(session): """Build the docs for this library.""" - session.install('-e', '.') - session.install('sphinx<3.0.0', 'alabaster', 'recommonmark') + session.install("-e", ".") + session.install("sphinx<3.0.0", "alabaster", "recommonmark") - shutil.rmtree(os.path.join('docs', '_build'), ignore_errors=True) + shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) session.run( - 'sphinx-build', - '-W', # warnings as errors - '-T', # show full traceback on exception - '-N', # no colors - '-b', 'html', - '-d', os.path.join('docs', '_build', 'doctrees', ''), - os.path.join('docs', ''), - os.path.join('docs', '_build', 'html', ''), + "sphinx-build", + "-W", # warnings as errors + "-T", # show full traceback on exception + "-N", # no colors + "-b", + "html", + "-d", + os.path.join("docs", "_build", "doctrees", ""), + os.path.join("docs", ""), + os.path.join("docs", "_build", "html", ""), ) diff --git a/samples/AUTHORING_GUIDE.md b/samples/AUTHORING_GUIDE.md new file mode 100644 index 00000000..55c97b32 --- /dev/null +++ b/samples/AUTHORING_GUIDE.md @@ -0,0 +1 @@ +See https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/AUTHORING_GUIDE.md \ No newline at end of file diff --git a/samples/CONTRIBUTING.md b/samples/CONTRIBUTING.md new file mode 100644 index 00000000..34c882b6 --- /dev/null +++ b/samples/CONTRIBUTING.md @@ -0,0 +1 @@ +See https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/CONTRIBUTING.md \ No newline at end of file diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py new file mode 100644 index 00000000..ba55d7ce --- /dev/null +++ b/samples/snippets/noxfile.py @@ -0,0 +1,224 @@ +# Copyright 2019 Google LLC +# +# 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. + +from __future__ import print_function + +import os +from pathlib import Path +import sys + +import nox + + +# WARNING - WARNING - WARNING - WARNING - WARNING +# WARNING - WARNING - WARNING - WARNING - WARNING +# DO NOT EDIT THIS FILE EVER! +# WARNING - WARNING - WARNING - WARNING - WARNING +# WARNING - WARNING - WARNING - WARNING - WARNING + +# Copy `noxfile_config.py` to your directory and modify it instead. + + +# `TEST_CONFIG` dict is a configuration hook that allows users to +# modify the test configurations. The values here should be in sync +# with `noxfile_config.py`. Users will copy `noxfile_config.py` into +# their directory and modify it. + +TEST_CONFIG = { + # You can opt out from the test for specific Python versions. + 'ignored_versions': ["2.7"], + + # An envvar key for determining the project id to use. Change it + # to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a + # build specific Cloud project. You can also use your own string + # to use your own Cloud project. + 'gcloud_project_env': 'GOOGLE_CLOUD_PROJECT', + # 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT', + + # A dictionary you want to inject into your test. Don't put any + # secrets here. These values will override predefined values. + 'envs': {}, +} + + +try: + # Ensure we can import noxfile_config in the project's directory. + sys.path.append('.') + from noxfile_config import TEST_CONFIG_OVERRIDE +except ImportError as e: + print("No user noxfile_config found: detail: {}".format(e)) + TEST_CONFIG_OVERRIDE = {} + +# Update the TEST_CONFIG with the user supplied values. +TEST_CONFIG.update(TEST_CONFIG_OVERRIDE) + + +def get_pytest_env_vars(): + """Returns a dict for pytest invocation.""" + ret = {} + + # Override the GCLOUD_PROJECT and the alias. + env_key = TEST_CONFIG['gcloud_project_env'] + # This should error out if not set. + ret['GOOGLE_CLOUD_PROJECT'] = os.environ[env_key] + + # Apply user supplied envs. + ret.update(TEST_CONFIG['envs']) + return ret + + +# DO NOT EDIT - automatically generated. +# All versions used to tested samples. +ALL_VERSIONS = ["2.7", "3.6", "3.7", "3.8"] + +# Any default versions that should be ignored. +IGNORED_VERSIONS = TEST_CONFIG['ignored_versions'] + +TESTED_VERSIONS = sorted([v for v in ALL_VERSIONS if v not in IGNORED_VERSIONS]) + +INSTALL_LIBRARY_FROM_SOURCE = bool(os.environ.get("INSTALL_LIBRARY_FROM_SOURCE", False)) +# +# Style Checks +# + + +def _determine_local_import_names(start_dir): + """Determines all import names that should be considered "local". + + This is used when running the linter to insure that import order is + properly checked. + """ + file_ext_pairs = [os.path.splitext(path) for path in os.listdir(start_dir)] + return [ + basename + for basename, extension in file_ext_pairs + if extension == ".py" + or os.path.isdir(os.path.join(start_dir, basename)) + and basename not in ("__pycache__") + ] + + +# Linting with flake8. +# +# We ignore the following rules: +# E203: whitespace before ‘:’ +# E266: too many leading ‘#’ for block comment +# E501: line too long +# I202: Additional newline in a section of imports +# +# We also need to specify the rules which are ignored by default: +# ['E226', 'W504', 'E126', 'E123', 'W503', 'E24', 'E704', 'E121'] +FLAKE8_COMMON_ARGS = [ + "--show-source", + "--builtin=gettext", + "--max-complexity=20", + "--import-order-style=google", + "--exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py", + "--ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202", + "--max-line-length=88", +] + + +@nox.session +def lint(session): + session.install("flake8", "flake8-import-order") + + local_names = _determine_local_import_names(".") + args = FLAKE8_COMMON_ARGS + [ + "--application-import-names", + ",".join(local_names), + "." + ] + session.run("flake8", *args) + + +# +# Sample Tests +# + + +PYTEST_COMMON_ARGS = ["--junitxml=sponge_log.xml"] + + +def _session_tests(session, post_install=None): + """Runs py.test for a particular project.""" + if os.path.exists("requirements.txt"): + session.install("-r", "requirements.txt") + + if os.path.exists("requirements-test.txt"): + session.install("-r", "requirements-test.txt") + + if INSTALL_LIBRARY_FROM_SOURCE: + session.install("-e", _get_repo_root()) + + if post_install: + post_install(session) + + session.run( + "pytest", + *(PYTEST_COMMON_ARGS + session.posargs), + # Pytest will return 5 when no tests are collected. This can happen + # on travis where slow and flaky tests are excluded. + # See http://doc.pytest.org/en/latest/_modules/_pytest/main.html + success_codes=[0, 5], + env=get_pytest_env_vars() + ) + + +@nox.session(python=ALL_VERSIONS) +def py(session): + """Runs py.test for a sample using the specified version of Python.""" + if session.python in TESTED_VERSIONS: + _session_tests(session) + else: + session.skip("SKIPPED: {} tests are disabled for this sample.".format( + session.python + )) + + +# +# Readmegen +# + + +def _get_repo_root(): + """ Returns the root folder of the project. """ + # Get root of this repository. Assume we don't have directories nested deeper than 10 items. + p = Path(os.getcwd()) + for i in range(10): + if p is None: + break + if Path(p / ".git").exists(): + return str(p) + p = p.parent + raise Exception("Unable to detect repository root.") + + +GENERATED_READMES = sorted([x for x in Path(".").rglob("*.rst.in")]) + + +@nox.session +@nox.parametrize("path", GENERATED_READMES) +def readmegen(session, path): + """(Re-)generates the readme for a sample.""" + session.install("jinja2", "pyyaml") + dir_ = os.path.dirname(path) + + if os.path.exists(os.path.join(dir_, "requirements.txt")): + session.install("-r", os.path.join(dir_, "requirements.txt")) + + in_file = os.path.join(dir_, "README.rst.in") + session.run( + "python", _get_repo_root() + "/scripts/readme-gen/readme_gen.py", in_file + ) diff --git a/samples/snippets/noxfile_config.py b/samples/snippets/noxfile_config.py new file mode 100644 index 00000000..cb62b824 --- /dev/null +++ b/samples/snippets/noxfile_config.py @@ -0,0 +1,41 @@ +# Copyright 2020 Google LLC +# +# 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. + +# Default TEST_CONFIG_OVERRIDE for python repos. + +# You can copy this file into your directory, then it will be inported from +# the noxfile.py. + +# The source of truth: +# https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/noxfile_config.py + +TEST_CONFIG_OVERRIDE = { + # You can opt out from the test for specific Python versions. + 'ignored_versions': ["2.7"], + + # An envvar key for determining the project id to use. Change it + # to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a + # build specific Cloud project. You can also use your own string + # to use your own Cloud project. + # 'gcloud_project_env': 'GOOGLE_CLOUD_PROJECT', + + # A dictionary you want to inject into your test. Don't put any + # secrets here. These values will override predefined values. + 'envs': { + 'GCLOUD_ORGANIZATION': '1081635000895', + 'GCLOUD_PROJECT': 'project-a-id', + 'GCLOUD_PUBSUB_TOPIC': 'projects/project-a-id/topics/notifications-sample-topic', + 'GCLOUD_PUBSUB_SUBSCRIPTION': 'notification_sample_subscription', + }, +} \ No newline at end of file diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt new file mode 100644 index 00000000..55b033e9 --- /dev/null +++ b/samples/snippets/requirements-test.txt @@ -0,0 +1 @@ +pytest \ No newline at end of file diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt new file mode 100644 index 00000000..4b59ce29 --- /dev/null +++ b/samples/snippets/requirements.txt @@ -0,0 +1,2 @@ +google-cloud-pubsub==1.6.0 +google-cloud-securitycenter==0.6.0 \ No newline at end of file diff --git a/samples/snippets/snippets_findings.py b/samples/snippets/snippets_findings.py new file mode 100644 index 00000000..dbd9406c --- /dev/null +++ b/samples/snippets/snippets_findings.py @@ -0,0 +1,602 @@ +#!/usr/bin/env python +# +# Copyright 2019 Google LLC +# +# 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 +# +# https://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. + +"""Examples of working with source and findings in Cloud Security Command Center.""" + +from itertools import chain +import os +import pytest + + +@pytest.fixture(scope="module") +def organization_id(): + """Get Organization ID from the environment variable """ + return os.environ["GCLOUD_ORGANIZATION"] + + +@pytest.fixture(scope="module") +def source_name(organization_id): + from google.cloud import securitycenter + + client = securitycenter.SecurityCenterClient() + org_name = "organizations/{org_id}".format(org_id=organization_id) + + source = client.create_source( + org_name, + { + "display_name": "Unit test source", + "description": "A new custom source that does X", + }, + ) + return source.name + + +def test_create_source(organization_id): + """Create a new findings source. """ + # [START create_source] + from google.cloud import securitycenter + + client = securitycenter.SecurityCenterClient() + # organization_id is the numeric ID of the organization. e.g.: + # organization_id = "111122222444" + org_name = "organizations/{org_id}".format(org_id=organization_id) + + created = client.create_source( + org_name, + { + "display_name": "Customized Display Name", + "description": "A new custom source that does X", + }, + ) + print("Created Source: {}".format(created.name)) + # [END create_source] + + +def test_get_source(source_name): + """Gets an existing source.""" + # [START get_source] + 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" + source = client.get_source(source_name) + + print("Source: {}".format(source)) + # [END get_source] + + +def test_update_source(source_name): + """Updates a source's display name.""" + # [START update_source] + from google.cloud import securitycenter + from google.protobuf import field_mask_pb2 + + client = securitycenter.SecurityCenterClient() + + # Field mask to only update the display name. + field_mask = field_mask_pb2.FieldMask(paths=["display_name"]) + + # 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" + updated = client.update_source( + {"name": source_name, "display_name": "Updated Display Name"}, + update_mask=field_mask, + ) + print("Updated Source: {}".format(updated)) + # [END update_source] + assert updated.display_name == "Updated Display Name" + + +def test_add_user_to_source(source_name): + """Gives a user findingsEditor permission to the source.""" + user_email = "csccclienttest@gmail.com" + # [START update_source_iam] + from google.cloud import securitycenter + from google.iam.v1 import policy_pb2 + + 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" + # Get the old policy so we can do an incremental update. + old_policy = client.get_iam_policy(source_name) + print("Old Policy: {}".format(old_policy)) + + # Setup a new IAM binding. + binding = policy_pb2.Binding() + binding.role = "roles/securitycenter.findingsEditor" + # user_email is an e-mail address known to Cloud IAM (e.g. a gmail address). + # user_mail = user@somedomain.com + binding.members.append("user:{}".format(user_email)) + + # Setting the e-tag avoids over-write existing policy + updated = client.set_iam_policy( + source_name, {"etag": old_policy.etag, "bindings": [binding]} + ) + + print("Updated Policy: {}".format(updated)) + + # [END update_source_iam] + assert any( + member == "user:csccclienttest@gmail.com" + for member in chain.from_iterable( + binding.members for binding in updated.bindings + ) + ) + + +def test_list_source(organization_id): + """Lists finding sources.""" + i = -1 + # [START list_sources] + from google.cloud import securitycenter + + # Create a new client. + client = securitycenter.SecurityCenterClient() + # organization_id is the numeric ID of the organization. e.g.: + # organization_id = "111122222444" + org_name = "organizations/{org_id}".format(org_id=organization_id) + + # Call the API and print out each existing source. + for i, source in enumerate(client.list_sources(org_name)): + print(i, source) + # [END list_sources] + assert i >= 0 + + +def test_create_finding(source_name): + """Creates a new finding.""" + # [START create_finding] + from google.cloud import securitycenter + from google.cloud.securitycenter_v1.proto.finding_pb2 import Finding + from google.protobuf.timestamp_pb2 import Timestamp + + # Create a new client. + client = securitycenter.SecurityCenterClient() + + # Use the current time as the finding "event time". + now_proto = Timestamp() + now_proto.GetCurrentTime() + + # 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" + + # Controlled by caller. + finding_id = "samplefindingid" + + # 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 = "//cloudresourcemanager.googleapis.com/organizations/11232" + + # Call The API. + created_finding = client.create_finding( + source_name, + finding_id, + { + "state": Finding.ACTIVE, + "resource_name": resource_name, + "category": "MEDIUM_RISK_ONE", + "event_time": now_proto, + }, + ) + print(created_finding) + # [END create_finding] + assert len(created_finding.name) > 0 + + +def test_create_finding_with_source_properties(source_name): + """Demonstrate creating a new finding with source properties. """ + # [START create_finding_with_properties] + from google.cloud import securitycenter + from google.cloud.securitycenter_v1.proto.finding_pb2 import Finding + from google.protobuf.timestamp_pb2 import Timestamp + from google.protobuf.struct_pb2 import Value + + # Create a new client. + client = securitycenter.SecurityCenterClient() + + # source_name is the resource path for a source that has been + # created previously (you can use list_sources to find a specific one). + # Its format is: + # source_name = "organizations/{organization_id}/sources/{source_id}" + # e.g.: + # source_name = "organizations/111122222444/sources/1234" + + # Controlled by caller. + finding_id = "samplefindingid2" + + # 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 = "//cloudresourcemanager.googleapis.com/organizations/11232" + + # Define source properties values as protobuf "Value" objects. + str_value = Value() + str_value.string_value = "string_example" + num_value = Value() + num_value.number_value = 1234 + + # Use the current time as the finding "event time". + now_proto = Timestamp() + now_proto.GetCurrentTime() + + created_finding = client.create_finding( + source_name, + finding_id, + { + "state": Finding.ACTIVE, + "resource_name": resource_name, + "category": "MEDIUM_RISK_ONE", + "source_properties": {"s_value": str_value, "n_value": num_value}, + "event_time": now_proto, + }, + ) + print(created_finding) + # [END create_finding_with_properties] + + +def test_update_finding(source_name): + # [START update_finding] + from google.cloud import securitycenter + from google.protobuf.struct_pb2 import Value + from google.protobuf import field_mask_pb2 + from google.protobuf.timestamp_pb2 import Timestamp + + client = securitycenter.SecurityCenterClient() + # Only update the specific source property and event_time. event_time + # is required for updates. + field_mask = field_mask_pb2.FieldMask( + paths=["source_properties.s_value", "event_time"] + ) + value = Value() + value.string_value = "new_string" + + # Set the update time to Now. This must be some time greater then the + # event_time on the original finding. + now_proto = Timestamp() + now_proto.GetCurrentTime() + + # 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" + finding_name = "{}/findings/samplefindingid2".format(source_name) + updated_finding = client.update_finding( + { + "name": finding_name, + "source_properties": {"s_value": value}, + "event_time": now_proto, + }, + update_mask=field_mask, + ) + + print( + "New Source properties: {}, Event Time {}".format( + updated_finding.source_properties, updated_finding.event_time.ToDatetime() + ) + ) + # [END update_finding] + + +def test_update_finding_state(source_name): + """Demonstrate updating only a finding state.""" + # [START update_finding_state] + from google.cloud import securitycenter + from google.cloud.securitycenter_v1.proto.finding_pb2 import Finding + from google.protobuf.timestamp_pb2 import Timestamp + from datetime import datetime + + # Create a client. + client = securitycenter.SecurityCenterClient() + # source_name is the resource path for a source that has been + # created previously (you can use list_sources to find a specific one). + # Its format is: + # source_name = "organizations/{organization_id}/sources/{source_id}" + # e.g.: + # source_name = "organizations/111122222444/sources/1234" + finding_name = "{}/findings/samplefindingid2".format(source_name) + + now_proto = Timestamp() + now_proto.GetCurrentTime() + + # Call the API to change the finding state to inactive as of now. + new_finding = client.set_finding_state( + finding_name, Finding.INACTIVE, start_time=now_proto + ) + print("New state: {}".format(Finding.State.Name(new_finding.state))) + # [END update_finding_state] + + +def test_trouble_shoot(source_name): + """Demonstrate calling test_iam_permissions to determine if the + service account has the correct permisions.""" + # [START test_iam_permissions] + from google.cloud import securitycenter + + # Create a client. + client = securitycenter.SecurityCenterClient() + # source_name is the resource path for a source that has been + # created previously (you can use list_sources to find a specific one). + # Its format is: + # source_name = "organizations/{organization_id}/sources/{source_id}" + # e.g.: + # source_name = "organizations/111122222444/sources/1234" + + # Check for permssions to call create_finding or update_finding. + permission_response = client.test_iam_permissions( + source_name, ["securitycenter.findings.update"] + ) + + print( + "Permision to create or update findings? {}".format( + len(permission_response.permissions) > 0 + ) + ) + # [END test_iam_permissions] + assert len(permission_response.permissions) > 0 + # [START test_iam_permissions] + # Check for permissions necessary to call set_finding_state. + permission_response = client.test_iam_permissions( + source_name, ["securitycenter.findings.setState"] + ) + print( + "Permision to update state? {}".format(len(permission_response.permissions) > 0) + ) + # [END test_iam_permissions] + assert len(permission_response.permissions) > 0 + + +def test_list_all_findings(organization_id): + # [START list_all_findings] + from google.cloud import securitycenter + + # Create a client. + client = securitycenter.SecurityCenterClient() + + # organization_id is the numeric ID of the organization. e.g.: + # organization_id = "111122222444" + org_name = "organizations/{org_id}".format(org_id=organization_id) + # The "sources/-" suffix lists findings across all sources. You + # also use a specific source_name instead. + all_sources = "{org_name}/sources/-".format(org_name=org_name) + finding_result_iterator = client.list_findings(all_sources) + for i, finding_result in enumerate(finding_result_iterator): + print( + "{}: name: {} resource: {}".format( + i, finding_result.finding.name, finding_result.finding.resource_name + ) + ) + # [END list_all_findings] + assert i > 0 + + +def test_list_filtered_findings(source_name): + # [START list_filtered_findings] + from google.cloud import securitycenter + + # Create a new client. + client = securitycenter.SecurityCenterClient() + + # source_name is the resource path for a source that has been + # created previously (you can use list_sources to find a specific one). + # Its format is: + # source_name = "organizations/{organization_id}/sources/{source_id}" + # e.g.: + # source_name = "organizations/111122222444/sources/1234" + # You an also use a wild-card "-" for all sources: + # source_name = "organizations/111122222444/sources/-" + finding_result_iterator = client.list_findings( + source_name, filter_='category="MEDIUM_RISK_ONE"' + ) + # Iterate an print all finding names and the resource they are + # in reference to. + for i, finding_result in enumerate(finding_result_iterator): + print( + "{}: name: {} resource: {}".format( + i, finding_result.finding.name, finding_result.finding.resource_name + ) + ) + # [END list_filtered_findings] + assert i > 0 + + +def test_list_findings_at_time(source_name): + # [START list_findings_at_a_time] + from google.cloud import securitycenter + from google.protobuf.timestamp_pb2 import Timestamp + from datetime import timedelta, datetime + + # Create a new client. + client = securitycenter.SecurityCenterClient() + + # source_name is the resource path for a source that has been + # created previously (you can use list_sources to find a specific one). + # Its format is: + # source_name = "organizations/{organization_id}/sources/{source_id}" + # e.g.: + # source_name = "organizations/111122222444/sources/1234" + # You an also use a wild-card "-" for all sources: + # source_name = "organizations/111122222444/sources/-" + five_days_ago = Timestamp() + five_days_ago.FromDatetime(datetime.now() - timedelta(days=5)) + # [END list_findings_at_a_time] + i = -1 + five_days_ago.FromDatetime(datetime(2019, 3, 5, 0, 0, 0)) + # [START list_findings_at_a_time] + + finding_result_iterator = client.list_findings(source_name, read_time=five_days_ago) + for i, finding_result in enumerate(finding_result_iterator): + print( + "{}: name: {} resource: {}".format( + i, finding_result.finding.name, finding_result.finding.resource_name + ) + ) + # [END list_findings_at_a_time] + assert i == -1 + + +def test_get_iam_policy(source_name): + """Gives a user findingsEditor permission to the source.""" + user_email = "csccclienttest@gmail.com" + # [START get_source_iam] + from google.cloud import securitycenter + from google.iam.v1 import policy_pb2 + + 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" + # Get the old policy so we can do an incremental update. + policy = client.get_iam_policy(source_name) + print("Policy: {}".format(policy)) + # [END get_source_iam] + + +def test_group_all_findings(organization_id): + """Demonstrates grouping all findings across an organization.""" + # [START group_all_findings] + from google.cloud import securitycenter + + # Create a client. + client = securitycenter.SecurityCenterClient() + + # organization_id is the numeric ID of the organization. e.g.: + # organization_id = "111122222444" + org_name = "organizations/{org_id}".format(org_id=organization_id) + # The "sources/-" suffix lists findings across all sources. You + # also use a specific source_name instead. + all_sources = "{org_name}/sources/-".format(org_name=org_name) + group_result_iterator = client.group_findings(all_sources, group_by="category") + for i, group_result in enumerate(group_result_iterator): + print((i + 1), group_result) + # [END group_all_findings] + assert i > 0 + + +def test_group_filtered_findings(source_name): + """Demonstrates grouping all findings across an organization.""" + # [START group_filtered_findings] + from google.cloud import securitycenter + + # Create a client. + client = securitycenter.SecurityCenterClient() + + # source_name is the resource path for a source that has been + # created previously (you can use list_sources to find a specific one). + # Its format is: + # source_name = "organizations/{organization_id}/sources/{source_id}" + # e.g.: + # source_name = "organizations/111122222444/sources/1234" + + group_result_iterator = client.group_findings( + source_name, group_by="category", filter_='state="ACTIVE"' + ) + for i, group_result in enumerate(group_result_iterator): + print((i + 1), group_result) + # [END group_filtered_findings] + assert i == 0 + + +def test_group_findings_at_time(source_name): + """Demonstrates grouping all findings across an organization as of + a specific time.""" + i = -1 + # [START group_findings_at_time] + from datetime import datetime, timedelta + from google.cloud import securitycenter + from google.protobuf.timestamp_pb2 import Timestamp + + # Create a client. + client = securitycenter.SecurityCenterClient() + + # source_name is the resource path for a source that has been + # created previously (you can use list_sources to find a specific one). + # Its format is: + # source_name = "organizations/{organization_id}/sources/{source_id}" + # e.g.: + # source_name = "organizations/111122222444/sources/1234" + + # Group findings as of yesterday. + read_time = datetime.utcnow() - timedelta(days=1) + timestamp_proto = Timestamp() + timestamp_proto.FromDatetime(read_time) + + group_result_iterator = client.group_findings( + source_name, group_by="category", read_time=timestamp_proto + ) + for i, group_result in enumerate(group_result_iterator): + print((i + 1), group_result) + # [END group_filtered_findings_at_time] + assert i == -1 + + +def test_group_findings_and_changes(source_name): + """Demonstrates grouping all findings across an organization and + associated changes.""" + # [START group_filtered_findings_with_changes] + from datetime import timedelta + + from google.cloud import securitycenter + from google.protobuf.duration_pb2 import Duration + + # Create a client. + client = securitycenter.SecurityCenterClient() + + # source_name is the resource path for a source that has been + # created previously (you can use list_sources to find a specific one). + # Its format is: + # source_name = "organizations/{organization_id}/sources/{source_id}" + # e.g.: + # source_name = "organizations/111122222444/sources/1234" + + # List assets and their state change the last 30 days + compare_delta = timedelta(days=30) + # Convert the timedelta to a Duration + duration_proto = Duration() + duration_proto.FromTimedelta(compare_delta) + + group_result_iterator = client.group_findings( + source_name, group_by="state_change", compare_duration=duration_proto + ) + for i, group_result in enumerate(group_result_iterator): + print((i + 1), group_result) + # [END group_findings_with_changes] + assert i == 0 diff --git a/samples/snippets/snippets_list_assets.py b/samples/snippets/snippets_list_assets.py new file mode 100644 index 00000000..ae50ff91 --- /dev/null +++ b/samples/snippets/snippets_list_assets.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python +# +# Copyright 2019 Google LLC +# +# 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 +# +# https://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. + +""" Examples of listing assets in Cloud Security Command Center.""" +import os +from datetime import datetime, timedelta +import pytest + + +@pytest.fixture(scope="module") +def organization_id(): + """Get Organization ID from the environment variable """ + return os.environ["GCLOUD_ORGANIZATION"] + + +def test_list_all_assets(organization_id): + """Demonstrate listing and printing all assets.""" + # [START demo_list_all_assets] + from google.cloud import securitycenter + + client = securitycenter.SecurityCenterClient() + # organization_id is the numeric ID of the organization. + # organization_id = "1234567777" + org_name = "organizations/{org_id}".format(org_id=organization_id) + + # Call the API and print results. + asset_iterator = client.list_assets(org_name) + for i, asset_result in enumerate(asset_iterator): + print(i, asset_result) + # [END demo_list_all_assets] + assert i > 0 + + +def test_list_assets_with_filters(organization_id): + """Demonstrate listing assets with a filter.""" + # [START demo_list_assets_with_filter] + from google.cloud import securitycenter + + client = securitycenter.SecurityCenterClient() + + # organization_id is the numeric ID of the organization. + # organization_id = "1234567777" + org_name = "organizations/{org_id}".format(org_id=organization_id) + + project_filter = ( + "security_center_properties.resource_type=" + + '"google.cloud.resourcemanager.Project"' + ) + # Call the API and print results. + asset_iterator = client.list_assets(org_name, filter_=project_filter) + for i, asset_result in enumerate(asset_iterator): + print(i, asset_result) + # [END demo_list_assets_with_filter] + assert i > 0 + + +def test_list_assets_with_filters_and_read_time(organization_id): + """Demonstrate listing assets with a filter.""" + # [START demo_list_assets_with_filter_and_time] + from datetime import datetime, timedelta + + from google.protobuf.timestamp_pb2 import Timestamp + + from google.cloud import securitycenter + + client = securitycenter.SecurityCenterClient() + + # organization_id is the numeric ID of the organization. + # organization_id = "1234567777" + org_name = "organizations/{org_id}".format(org_id=organization_id) + + project_filter = ( + "security_center_properties.resource_type=" + + '"google.cloud.resourcemanager.Project"' + ) + + # Lists assets as of yesterday. + read_time = datetime.utcnow() - timedelta(days=1) + timestamp_proto = Timestamp() + timestamp_proto.FromDatetime(read_time) + + # Call the API and print results. + asset_iterator = client.list_assets( + org_name, filter_=project_filter, read_time=timestamp_proto + ) + for i, asset_result in enumerate(asset_iterator): + print(i, asset_result) + # [END demo_list_assets_with_filter_and_time] + assert i > 0 + + +def test_list_point_in_time_changes(organization_id): + """Demonstrate listing assets along with their state changes.""" + # [START demo_list_assets_changes] + from datetime import timedelta + + from google.protobuf.duration_pb2 import Duration + from google.cloud import securitycenter + + client = securitycenter.SecurityCenterClient() + + # organization_id is the numeric ID of the organization. + # organization_id = "1234567777" + org_name = "organizations/{org_id}".format(org_id=organization_id) + project_filter = ( + "security_center_properties.resource_type=" + + '"google.cloud.resourcemanager.Project"' + ) + + # List assets and their state change the last 30 days + compare_delta = timedelta(days=30) + # Convert the timedelta to a Duration + duration_proto = Duration() + duration_proto.FromTimedelta(compare_delta) + # Call the API and print results. + asset_iterator = client.list_assets( + org_name, filter_=project_filter, compare_duration=duration_proto + ) + for i, asset in enumerate(asset_iterator): + print(i, asset) + + # [END demo_list_assets_changes] + assert i > 0 + + +def test_group_assets(organization_id): + """Demonstrates grouping all assets by type. """ + # [START group_all_assets] + from google.cloud import securitycenter + + client = securitycenter.SecurityCenterClient() + + # organization_id is the numeric ID of the organization. + # organization_id = "1234567777" + org_name = "organizations/{org_id}".format(org_id=organization_id) + + group_by_type = "security_center_properties.resource_type" + + result_iterator = client.group_assets(org_name, group_by=group_by_type) + for i, result in enumerate(result_iterator): + print((i + 1), result) + # [END group_all_assets] + # 8 different asset types. + assert i >= 8 + + +def test_group_filtered_assets(organization_id): + """Demonstrates grouping assets by type with a filter. """ + # [START group_all_assets] + from google.cloud import securitycenter + + client = securitycenter.SecurityCenterClient() + + # organization_id is the numeric ID of the organization. + # organization_id = "1234567777" + org_name = "organizations/{org_id}".format(org_id=organization_id) + + group_by_type = "security_center_properties.resource_type" + only_projects = ( + "security_center_properties.resource_type=" + + '"google.cloud.resourcemanager.Project"' + ) + result_iterator = client.group_assets( + org_name, group_by=group_by_type, filter_=only_projects + ) + for i, result in enumerate(result_iterator): + print((i + 1), result) + # [END group_all_assets] + # only one asset type is a project + assert i == 0 + + +def test_group_assets_by_changes(organization_id): + """Demonstrates grouping assets by there changes over a period of time.""" + # [START group_all_assets_by_change] + from datetime import timedelta + + from google.cloud import securitycenter + from google.protobuf.duration_pb2 import Duration + + client = securitycenter.SecurityCenterClient() + + duration_proto = Duration() + duration_proto.FromTimedelta(timedelta(days=5)) + + # organization_id is the numeric ID of the organization. + # organization_id = "1234567777" + org_name = "organizations/{org_id}".format(org_id=organization_id) + result_iterator = client.group_assets( + org_name, group_by="state_change", compare_duration=duration_proto + ) + for i, result in enumerate(result_iterator): + print((i + 1), result) + # [END group_all_assets_by_change] + # only one asset type is a project + assert i >= 0 diff --git a/samples/snippets/snippets_notification_configs.py b/samples/snippets/snippets_notification_configs.py new file mode 100644 index 00000000..42d1f70f --- /dev/null +++ b/samples/snippets/snippets_notification_configs.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python +# +# Copyright 2020 Google LLC +# +# 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 +# +# https://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. +"""Demos for working with notification configs.""" + + +def create_notification_config(organization_id, notification_config_id, pubsub_topic): + + # [START scc_create_notification_config] + from google.cloud import securitycenter as securitycenter + + client = securitycenter.SecurityCenterClient() + + # TODO: organization_id = "your-org-id" + # TODO: notification_config_id = "your-config-id" + # TODO: pubsub_topic = "projects/{your-project-id}/topics/{your-topic-ic}" + # Ensure this ServiceAccount has the "pubsub.topics.setIamPolicy" permission on the new topic. + + org_name = "organizations/{org_id}".format(org_id=organization_id) + + created_notification_config = client.create_notification_config( + org_name, + notification_config_id, + { + "description": "Notification for active findings", + "pubsub_topic": pubsub_topic, + "streaming_config": {"filter": 'state = "ACTIVE"',}, + }, + ) + + print(created_notification_config) + # [END scc_create_notification_config] + return created_notification_config + + +def delete_notification_config(organization_id, notification_config_id): + + # [START scc_delete_notification_config] + from google.cloud import securitycenter as securitycenter + + client = securitycenter.SecurityCenterClient() + + # TODO: organization_id = "your-org-id" + # TODO: notification_config_id = "your-config-id" + + notification_config_name = "organizations/{org_id}/notificationConfigs/{config_id}".format( + org_id=organization_id, config_id=notification_config_id + ) + + client.delete_notification_config(notification_config_name) + print("Deleted notification config: {}".format(notification_config_name)) + # [END scc_delete_notification_config] + return True + + +def get_notification_config(organization_id, notification_config_id): + + # [START scc_get_notification_config] + from google.cloud import securitycenter as securitycenter + + client = securitycenter.SecurityCenterClient() + + # TODO: organization_id = "your-org-id" + # TODO: notification_config_id = "your-config-id" + + notification_config_name = "organizations/{org_id}/notificationConfigs/{config_id}".format( + org_id=organization_id, config_id=notification_config_id + ) + + notification_config = client.get_notification_config(notification_config_name) + print("Got notification config: {}".format(notification_config)) + # [END scc_get_notification_config] + return notification_config + + +def list_notification_configs(organization_id): + + # [START scc_list_notification_configs] + from google.cloud import securitycenter as securitycenter + + client = securitycenter.SecurityCenterClient() + + # TODO: organization_id = "your-org-id" + org_name = "organizations/{org_id}".format(org_id=organization_id) + + notification_configs_iterator = client.list_notification_configs(org_name) + for i, config in enumerate(notification_configs_iterator): + print("{}: notification_config: {}".format(i, config)) + # [END scc_list_notification_configs] + return notification_configs_iterator + + +def update_notification_config(organization_id, notification_config_id, pubsub_topic): + # [START scc_update_notification_config] + from google.cloud import securitycenter as securitycenter + from google.protobuf import field_mask_pb2 + + client = securitycenter.SecurityCenterClient() + + # TODO organization_id = "your-org-id" + # TODO notification_config_id = "config-id-to-update" + # TODO pubsub_topic = "projects/{new-project}/topics/{new-topic}" + # If updating a pubsub_topic, ensure this ServiceAccount has the + # "pubsub.topics.setIamPolicy" permission on the new topic. + + notification_config_name = "organizations/{org_id}/notificationConfigs/{config_id}".format( + org_id=organization_id, config_id=notification_config_id + ) + + updated_description = "New updated description" + + # Only description and pubsub_topic can be updated. + field_mask = field_mask_pb2.FieldMask(paths=["description", "pubsub_topic"]) + + updated_notification_config = client.update_notification_config( + { + "name": notification_config_name, + "description": updated_description, + "pubsub_topic": pubsub_topic, + }, + update_mask=field_mask, + ) + + print(updated_notification_config) + # [END scc_update_notification_config] + return updated_notification_config diff --git a/samples/snippets/snippets_notification_receiver.py b/samples/snippets/snippets_notification_receiver.py new file mode 100644 index 00000000..fd2fdcae --- /dev/null +++ b/samples/snippets/snippets_notification_receiver.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# Copyright 2020 Google LLC +# +# 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 +# +# https://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. +"""Demo for receiving notifications.""" + + +def receive_notifications(project_id, subscription_name): + # [START scc_receive_notifications] + # Requires https://cloud.google.com/pubsub/docs/quickstart-client-libraries#pubsub-client-libraries-python + from google.cloud import pubsub_v1 + from google.cloud.securitycenter_v1.proto.notification_message_pb2 import ( + NotificationMessage, + ) + from google.protobuf import json_format + + # TODO: project_id = "your-project-id" + # TODO: subscription_name = "your-subscription-name" + + def callback(message): + print("Received message") + + notification_msg = NotificationMessage() + json_format.Parse(message.data, notification_msg) + + print( + "Notification config name: {}".format( + notification_msg.notification_config_name + ) + ) + print("Finding: {}".format(notification_msg.finding)) + + # Ack the message to prevent it from being pulled again + message.ack() + + subscriber = pubsub_v1.SubscriberClient() + subscription_path = subscriber.subscription_path(project_id, subscription_name) + + streaming_pull_future = subscriber.subscribe(subscription_path, callback=callback) + + print("Listening for messages on {}...\n".format(subscription_path)) + try: + streaming_pull_future.result(timeout=1) # Block for 1 second + except: + streaming_pull_future.cancel() + # [END scc_receive_notifications] + return True diff --git a/samples/snippets/snippets_notification_test.py b/samples/snippets/snippets_notification_test.py new file mode 100644 index 00000000..d24d0906 --- /dev/null +++ b/samples/snippets/snippets_notification_test.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python +# +# Copyright 2020 Google LLC +# +# 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 +# +# https://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. +"""Tests for snippets.""" + +import os +import uuid + +from google.cloud import securitycenter as securitycenter +import pytest + +import snippets_notification_configs +import snippets_notification_receiver + +ORG_ID = os.environ["GCLOUD_ORGANIZATION"] +PROJECT_ID = os.environ["GCLOUD_PROJECT"] +PUBSUB_TOPIC = os.environ["GCLOUD_PUBSUB_TOPIC"] +PUBSUB_SUBSCRIPTION = os.environ["GCLOUD_PUBSUB_SUBSCRIPTION"] + +CREATE_CONFIG_ID = "new-notification-pytest" + str(uuid.uuid1()) +DELETE_CONFIG_ID = "new-notification-pytest" + str(uuid.uuid1()) +GET_CONFIG_ID = "new-notification-pytest" + str(uuid.uuid1()) +UPDATE_CONFIG_ID = "new-notification-pytest" + str(uuid.uuid1()) + + +def cleanup_notification_config(notification_config_id): + client = securitycenter.SecurityCenterClient() + + notification_config_name = "organizations/{org_id}/notificationConfigs/{config_id}".format( + org_id=ORG_ID, config_id=notification_config_id + ) + client.delete_notification_config(notification_config_name) + + +@pytest.fixture +def new_notification_config_for_update(): + client = securitycenter.SecurityCenterClient() + + org_name = "organizations/{org_id}".format(org_id=ORG_ID) + + created_notification_config = client.create_notification_config( + org_name, + UPDATE_CONFIG_ID, + { + "description": "Notification for active findings", + "pubsub_topic": PUBSUB_TOPIC, + "streaming_config": {"filter": "",}, + }, + ) + yield created_notification_config + cleanup_notification_config(UPDATE_CONFIG_ID) + + +@pytest.fixture +def new_notification_config_for_get(): + client = securitycenter.SecurityCenterClient() + + org_name = "organizations/{org_id}".format(org_id=ORG_ID) + + created_notification_config = client.create_notification_config( + org_name, + GET_CONFIG_ID, + { + "description": "Notification for active findings", + "pubsub_topic": PUBSUB_TOPIC, + "streaming_config": {"filter": "",}, + }, + ) + yield created_notification_config + cleanup_notification_config(GET_CONFIG_ID) + + +@pytest.fixture +def deleted_notification_config(): + client = securitycenter.SecurityCenterClient() + + org_name = "organizations/{org_id}".format(org_id=ORG_ID) + + created_notification_config = client.create_notification_config( + org_name, + DELETE_CONFIG_ID, + { + "description": "Notification for active findings", + "pubsub_topic": PUBSUB_TOPIC, + "streaming_config": {"filter": "",}, + }, + ) + return created_notification_config + + +def test_create_notification_config(): + created_notification_config = snippets_notification_configs.create_notification_config( + ORG_ID, CREATE_CONFIG_ID, PUBSUB_TOPIC + ) + assert created_notification_config is not None + + cleanup_notification_config(CREATE_CONFIG_ID) + + +def test_delete_notification_config(deleted_notification_config): + assert ( + snippets_notification_configs.delete_notification_config( + ORG_ID, DELETE_CONFIG_ID + ) + == True + ) + + +def test_get_notification_config(new_notification_config_for_get): + retrieved_config = snippets_notification_configs.get_notification_config( + ORG_ID, GET_CONFIG_ID + ) + assert retrieved_config is not None + + +def test_list_notification_configs(): + iterator = snippets_notification_configs.list_notification_configs(ORG_ID) + assert iterator is not None + + +def test_update_notification_config(new_notification_config_for_update): + updated_config = snippets_notification_configs.update_notification_config( + ORG_ID, UPDATE_CONFIG_ID, PUBSUB_TOPIC + ) + assert updated_config is not None + + +def test_receive_notifications(): + assert ( + snippets_notification_receiver.receive_notifications( + PROJECT_ID, PUBSUB_SUBSCRIPTION + ) + == True + ) diff --git a/samples/snippets/snippets_orgs.py b/samples/snippets/snippets_orgs.py new file mode 100644 index 00000000..7781da11 --- /dev/null +++ b/samples/snippets/snippets_orgs.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# +# Copyright 2019 Google LLC +# +# 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 +# +# https://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. +"""Examples for working with organization settings. """ +import os +import pytest + + +@pytest.fixture(scope="module") +def organization_id(): + """Get Organization ID from the environment variable """ + return os.environ["GCLOUD_ORGANIZATION"] + + +def test_get_settings(organization_id): + """Example showing how to retreive current organization settings.""" + # [START get_org_settings] + from google.cloud import securitycenter + + client = securitycenter.SecurityCenterClient() + # organization_id is numeric ID for the organization. e.g. + # organization_id = "111112223333" + + org_settings_name = client.organization_settings_path(organization_id) + + org_settings = client.get_organization_settings(org_settings_name) + print(org_settings) + # [END get_org_settings] + + +def test_update_asset_discovery_org_settings(organization_id): + """Example showing how to update the asset discovery configuration + for an organization.""" + # [START update_org_settings] + from google.cloud import securitycenter + from google.protobuf import field_mask_pb2 + + # Create the client + client = securitycenter.SecurityCenterClient() + # organization_id is numeric ID for the organization. e.g. + # organization_id = "111112223333" + org_settings_name = "organizations/{org_id}/organizationSettings".format( + org_id=organization_id + ) + # Only update the enable_asset_discovery_value (leave others untouched). + field_mask = field_mask_pb2.FieldMask(paths=["enable_asset_discovery"]) + # Call the service. + updated = client.update_organization_settings( + {"name": org_settings_name, "enable_asset_discovery": True}, + update_mask=field_mask, + ) + print("Asset Discovery Enabled? {}".format(updated.enable_asset_discovery)) + # [END update_org_settings] + assert updated.enable_asset_discovery == True diff --git a/samples/snippets/snippets_security_marks.py b/samples/snippets/snippets_security_marks.py new file mode 100644 index 00000000..ef2f5d73 --- /dev/null +++ b/samples/snippets/snippets_security_marks.py @@ -0,0 +1,272 @@ +#!/usr/bin/env python +# +# Copyright 2019 Google LLC +# +# 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 +# +# https://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. +"""Demos for working with security marks.""" +import os +import random + +import pytest + + +@pytest.fixture(scope="module") +def organization_id(): + """Gets Organization ID from the environment variable """ + return os.environ["GCLOUD_ORGANIZATION"] + + +@pytest.fixture(scope="module") +def asset_name(organization_id): + """Returns a random asset name from existing assets.""" + from google.cloud import securitycenter + + client = securitycenter.SecurityCenterClient() + # organization_id is the numeric ID of the organization. + # organization_id=1234567777 + org_name = "organizations/{org_id}".format(org_id=organization_id) + assets = list(client.list_assets(org_name)) + # Select a random asset to avoid collision between integration tests. + asset = (random.sample(assets, 1)[0]).asset.name + + # Set fresh marks. + update = client.update_security_marks( + {"name": "{}/securityMarks".format(asset), "marks": {"other": "other_val"}} + ) + assert update.marks == {"other": "other_val"} + return asset + + +@pytest.fixture(scope="module") +def source_name(organization_id): + """Creates a new source in the organization.""" + from google.cloud import securitycenter + + client = securitycenter.SecurityCenterClient() + org_name = "organizations/{org_id}".format(org_id=organization_id) + source = client.create_source( + org_name, + { + "display_name": "Security marks Unit test source", + "description": "A new custom source that does X", + }, + ) + return source.name + + +@pytest.fixture(scope="module") +def finding_name(source_name): + """Creates a new finding and returns it name.""" + from google.cloud import securitycenter + from google.cloud.securitycenter_v1.proto.finding_pb2 import Finding + from google.protobuf.timestamp_pb2 import Timestamp + + client = securitycenter.SecurityCenterClient() + + now_proto = Timestamp() + now_proto.GetCurrentTime() + + finding = client.create_finding( + source_name, + "scfinding", + { + "state": Finding.ACTIVE, + "category": "C1", + "event_time": now_proto, + "resource_name": "//cloudresourcemanager.googleapis.com/organizations/1234", + }, + ) + client.create_finding( + source_name, + "untouched", + { + "state": Finding.ACTIVE, + "category": "MEDIUM_RISK_ONE", + "event_time": now_proto, + "resource_name": "//cloudresourcemanager.googleapis.com/organizations/1234", + }, + ) + + return finding.name + + +def test_add_to_asset(asset_name): + """Add new security marks to an asset.""" + # [START add_marks_to_asset] + 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 = "{}/securityMarks".format(asset_name) + + # 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( + {"name": marks_name, "marks": marks}, + # If this field was left empty, all marks would be cleared before adding + # the new values. + update_mask=field_mask, + ) + print(updated_marks) + # [END add_marks_to_asset] + assert updated_marks.marks.keys() >= marks.keys() + + +def test_clear_from_asset(asset_name): + """Removes security marks from an asset.""" + # Make sure they are there first + test_add_to_asset(asset_name) + # [START clear_marks_asset] + 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 = "{}/securityMarks".format(asset_name) + + field_mask = field_mask_pb2.FieldMask(paths=["marks.key_a", "marks.key_b"]) + + updated_marks = client.update_security_marks( + { + "name": marks_name + # Note, no marks specified, so the specified values in + # the fields masks will be deleted. + }, + # If this field was left empty, all marks would be cleared. + update_mask=field_mask, + ) + print(updated_marks) + # [END clear_marks_asset] + assert "other" in updated_marks.marks + assert len(updated_marks.marks) == 1 + + +def test_delete_and_update_marks(asset_name): + """Updates and deletes security marks from an asset in the same call.""" + # Make sure they are there first + test_add_to_asset(asset_name) + # [START delete_and_update_marks] + 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 = "{}/securityMarks".format(asset_name) + + 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( + {"name": marks_name, "marks": marks}, update_mask=field_mask + ) + print(updated_marks) + # [END delete_and_update_marks] + assert updated_marks.marks == {"key_a": "new_value_for_a", "other": "other_val"} + + +def test_add_to_finding(finding_name): + """Adds security marks to a finding. """ + # [START add_marks_to_finding] + 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 = "{}/securityMarks".format(finding_name) + + # 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( + {"name": finding_marks_name, "marks": marks}, update_mask=field_mask + ) + # [END add_marks_to_finding] + + assert updated_marks.marks == marks + + +def test_list_assets_with_query_marks(organization_id, asset_name): + """Lists assets with a filter on security marks. """ + test_add_to_asset(asset_name) + # [START demo_list_assets_with_security_marks] + from google.cloud import securitycenter + + client = securitycenter.SecurityCenterClient() + + # organization_id is the numeric ID of the organization. + # organization_id=1234567777 + org_name = "organizations/{org_id}".format(org_id=organization_id) + + marks_filter = 'security_marks.marks.key_a = "value_a"' + # Call the API and print results. + asset_iterator = client.list_assets(org_name, filter_=marks_filter) + + # Call the API and print results. + asset_iterator = client.list_assets(org_name, filter_=marks_filter) + for i, asset_result in enumerate(asset_iterator): + print(i, asset_result) + # [END demo_list_assets_with_security_marks] + assert i >= 0 + + +def test_list_findings_with_query_marks(source_name, finding_name): + """Lists findings with a filter on security marks.""" + # ensure marks are set on finding. + test_add_to_finding(finding_name) + i = -1 + # [START demo_list_findings_with_security_marks] + 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(source_name, filter_=marks_filter) + for i, finding_result in enumerate(finding_iterator): + print(i, finding_result) + # [END demo_list_findings_with_security_marks] + # one finding should have been updated with keys, and one should be + # untouched. + assert i == 0 diff --git a/synth.metadata b/synth.metadata index f8f1ed06..c9656a59 100644 --- a/synth.metadata +++ b/synth.metadata @@ -3,23 +3,30 @@ { "git": { "name": ".", - "remote": "https://github.com/googleapis/python-securitycenter.git", - "sha": "c930e6afc6aa701761f9966e1391ca2d3ebb30f4" + "remote": "git@github.com:googleapis/python-securitycenter.git", + "sha": "a30a996cafb8dd9fed3c86ef641d42ab959febe3" } }, { "git": { "name": "googleapis", "remote": "https://github.com/googleapis/googleapis.git", - "sha": "b882b8e6bfcd708042ff00f7adc67ce750817dd0", - "internalRef": "318028816" + "sha": "50ae1c72fd94a3ae4269394b09e4b7fbb9251146", + "internalRef": "320484049" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "303271797a360f8a439203413f13a160f2f5b3b4" + "sha": "799d8e6522c1ef7cb55a70d9ea0b15e045c3d00b" + } + }, + { + "git": { + "name": "synthtool", + "remote": "https://github.com/googleapis/synthtool.git", + "sha": "799d8e6522c1ef7cb55a70d9ea0b15e045c3d00b" } } ], diff --git a/synth.py b/synth.py index 8f230a46..576b74a6 100644 --- a/synth.py +++ b/synth.py @@ -15,6 +15,7 @@ """This script is used to synthesize generated parts of this library.""" import synthtool as s from synthtool import gcp +from synthtool.languages import python gapic = gcp.GAPICBazel() common = gcp.CommonTemplates() @@ -48,8 +49,10 @@ # ---------------------------------------------------------------------------- # Add templated files # ---------------------------------------------------------------------------- -templated_files = common.py_library(cov_level=88) -s.move(templated_files, excludes=['noxfile.py']) +templated_files = common.py_library(cov_level=88, samples=True) +s.move(templated_files) + +python.py_samples(root="samples", skip_readmes=True) # TODO(busunkim): Use latest sphinx after microgenerator transition s.replace("noxfile.py", """['"]sphinx['"]""", '"sphinx<3.0.0"') From b704ead19a3c53bc739ebb512b3bed1957e3fb38 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Fri, 10 Jul 2020 20:04:09 +0000 Subject: [PATCH 2/9] chore: lint setup.py --- setup.py | 57 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/setup.py b/setup.py index d052d865..5a2cf044 100644 --- a/setup.py +++ b/setup.py @@ -19,59 +19,58 @@ import setuptools -name = 'google-cloud-securitycenter' -description = 'Cloud Security Command Center API API client library' +name = "google-cloud-securitycenter" +description = "Cloud Security Command Center API API client library" version = "0.6.0" -release_status = 'Development Status :: 3 - Alpha' +release_status = "Development Status :: 3 - Alpha" dependencies = [ - 'google-api-core[grpc] >= 1.14.0, < 2.0.0dev', - 'grpc-google-iam-v1 >= 0.12.3, < 0.13dev', + "google-api-core[grpc] >= 1.14.0, < 2.0.0dev", + "grpc-google-iam-v1 >= 0.12.3, < 0.13dev", 'enum34; python_version < "3.4"', ] package_root = os.path.abspath(os.path.dirname(__file__)) -readme_filename = os.path.join(package_root, 'README.rst') -with io.open(readme_filename, encoding='utf-8') as readme_file: +readme_filename = os.path.join(package_root, "README.rst") +with io.open(readme_filename, encoding="utf-8") as readme_file: readme = readme_file.read() packages = [ - package for package in setuptools.find_packages() - if package.startswith('google') + package for package in setuptools.find_packages() if package.startswith("google") ] -namespaces = ['google'] -if 'google.cloud' in packages: - namespaces.append('google.cloud') +namespaces = ["google"] +if "google.cloud" in packages: + namespaces.append("google.cloud") setuptools.setup( name=name, version=version, description=description, long_description=readme, - author='Google LLC', - author_email='googleapis-packages@google.com', - license='Apache 2.0', - url='https://github.com/googleapis/python-securitycenter', + author="Google LLC", + author_email="googleapis-packages@google.com", + license="Apache 2.0", + url="https://github.com/googleapis/python-securitycenter", classifiers=[ release_status, - 'Intended Audience :: Developers', - 'License :: OSI Approved :: Apache Software License', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Operating System :: OS Independent', - 'Topic :: Internet', + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Operating System :: OS Independent", + "Topic :: Internet", ], - platforms='Posix; MacOS X; Windows', + platforms="Posix; MacOS X; Windows", packages=packages, namespace_packages=namespaces, install_requires=dependencies, - python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*', + python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*", include_package_data=True, zip_safe=False, ) From e7f2bc826881f3456d296c3762f5b09e86d512bf Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Fri, 10 Jul 2020 21:39:43 +0000 Subject: [PATCH 3/9] test: split tests out into a separate file --- samples/snippets/snippets_findings.py | 88 +++++------- samples/snippets/snippets_findings_test.py | 116 ++++++++++++++++ samples/snippets/snippets_list_assets.py | 43 +++--- samples/snippets/snippets_list_assets_test.py | 57 ++++++++ samples/snippets/snippets_orgs.py | 14 +- samples/snippets/snippets_orgs_test.py | 32 +++++ .../snippets/snippets_security_marks_test.py | 125 ++++++++++++++++++ 7 files changed, 384 insertions(+), 91 deletions(-) create mode 100644 samples/snippets/snippets_findings_test.py create mode 100644 samples/snippets/snippets_list_assets_test.py create mode 100644 samples/snippets/snippets_orgs_test.py create mode 100644 samples/snippets/snippets_security_marks_test.py diff --git a/samples/snippets/snippets_findings.py b/samples/snippets/snippets_findings.py index dbd9406c..5a5cb8f3 100644 --- a/samples/snippets/snippets_findings.py +++ b/samples/snippets/snippets_findings.py @@ -16,35 +16,9 @@ """Examples of working with source and findings in Cloud Security Command Center.""" -from itertools import chain -import os -import pytest -@pytest.fixture(scope="module") -def organization_id(): - """Get Organization ID from the environment variable """ - return os.environ["GCLOUD_ORGANIZATION"] - - -@pytest.fixture(scope="module") -def source_name(organization_id): - from google.cloud import securitycenter - - client = securitycenter.SecurityCenterClient() - org_name = "organizations/{org_id}".format(org_id=organization_id) - - source = client.create_source( - org_name, - { - "display_name": "Unit test source", - "description": "A new custom source that does X", - }, - ) - return source.name - - -def test_create_source(organization_id): +def create_source(organization_id): """Create a new findings source. """ # [START create_source] from google.cloud import securitycenter @@ -65,7 +39,7 @@ def test_create_source(organization_id): # [END create_source] -def test_get_source(source_name): +def get_source(source_name): """Gets an existing source.""" # [START get_source] from google.cloud import securitycenter @@ -82,9 +56,10 @@ def test_get_source(source_name): print("Source: {}".format(source)) # [END get_source] + return source -def test_update_source(source_name): +def update_source(source_name): """Updates a source's display name.""" # [START update_source] from google.cloud import securitycenter @@ -107,10 +82,10 @@ def test_update_source(source_name): ) print("Updated Source: {}".format(updated)) # [END update_source] - assert updated.display_name == "Updated Display Name" + return updated -def test_add_user_to_source(source_name): +def add_user_to_source(source_name): """Gives a user findingsEditor permission to the source.""" user_email = "csccclienttest@gmail.com" # [START update_source_iam] @@ -144,15 +119,11 @@ def test_add_user_to_source(source_name): print("Updated Policy: {}".format(updated)) # [END update_source_iam] - assert any( - member == "user:csccclienttest@gmail.com" - for member in chain.from_iterable( - binding.members for binding in updated.bindings - ) - ) + return binding, updated -def test_list_source(organization_id): + +def list_source(organization_id): """Lists finding sources.""" i = -1 # [START list_sources] @@ -168,10 +139,10 @@ def test_list_source(organization_id): for i, source in enumerate(client.list_sources(org_name)): print(i, source) # [END list_sources] - assert i >= 0 + -def test_create_finding(source_name): +def create_finding(source_name): """Creates a new finding.""" # [START create_finding] from google.cloud import securitycenter @@ -213,10 +184,10 @@ def test_create_finding(source_name): ) print(created_finding) # [END create_finding] - assert len(created_finding.name) > 0 + return created_finding -def test_create_finding_with_source_properties(source_name): +def create_finding_with_source_properties(source_name): """Demonstrate creating a new finding with source properties. """ # [START create_finding_with_properties] from google.cloud import securitycenter @@ -267,7 +238,7 @@ def test_create_finding_with_source_properties(source_name): # [END create_finding_with_properties] -def test_update_finding(source_name): +def update_finding(source_name): # [START update_finding] from google.cloud import securitycenter from google.protobuf.struct_pb2 import Value @@ -312,7 +283,7 @@ def test_update_finding(source_name): # [END update_finding] -def test_update_finding_state(source_name): +def update_finding_state(source_name): """Demonstrate updating only a finding state.""" # [START update_finding_state] from google.cloud import securitycenter @@ -341,7 +312,7 @@ def test_update_finding_state(source_name): # [END update_finding_state] -def test_trouble_shoot(source_name): +def trouble_shoot(source_name): """Demonstrate calling test_iam_permissions to determine if the service account has the correct permisions.""" # [START test_iam_permissions] @@ -377,10 +348,11 @@ def test_trouble_shoot(source_name): "Permision to update state? {}".format(len(permission_response.permissions) > 0) ) # [END test_iam_permissions] + return permission_response assert len(permission_response.permissions) > 0 -def test_list_all_findings(organization_id): +def list_all_findings(organization_id): # [START list_all_findings] from google.cloud import securitycenter @@ -401,10 +373,10 @@ def test_list_all_findings(organization_id): ) ) # [END list_all_findings] - assert i > 0 + return i -def test_list_filtered_findings(source_name): +def list_filtered_findings(source_name): # [START list_filtered_findings] from google.cloud import securitycenter @@ -431,10 +403,10 @@ def test_list_filtered_findings(source_name): ) ) # [END list_filtered_findings] - assert i > 0 + return i -def test_list_findings_at_time(source_name): +def list_findings_at_time(source_name): # [START list_findings_at_a_time] from google.cloud import securitycenter from google.protobuf.timestamp_pb2 import Timestamp @@ -466,12 +438,11 @@ def test_list_findings_at_time(source_name): ) ) # [END list_findings_at_a_time] - assert i == -1 + return i -def test_get_iam_policy(source_name): +def get_iam_policy(source_name): """Gives a user findingsEditor permission to the source.""" - user_email = "csccclienttest@gmail.com" # [START get_source_iam] from google.cloud import securitycenter from google.iam.v1 import policy_pb2 @@ -490,8 +461,9 @@ def test_get_iam_policy(source_name): # [END get_source_iam] -def test_group_all_findings(organization_id): +def group_all_findings(organization_id): """Demonstrates grouping all findings across an organization.""" + i = 0 # [START group_all_findings] from google.cloud import securitycenter @@ -511,8 +483,9 @@ def test_group_all_findings(organization_id): assert i > 0 -def test_group_filtered_findings(source_name): +def group_filtered_findings(source_name): """Demonstrates grouping all findings across an organization.""" + i = 0 # [START group_filtered_findings] from google.cloud import securitycenter @@ -535,7 +508,7 @@ def test_group_filtered_findings(source_name): assert i == 0 -def test_group_findings_at_time(source_name): +def group_findings_at_time(source_name): """Demonstrates grouping all findings across an organization as of a specific time.""" i = -1 @@ -568,9 +541,10 @@ def test_group_findings_at_time(source_name): assert i == -1 -def test_group_findings_and_changes(source_name): +def group_findings_and_changes(source_name): """Demonstrates grouping all findings across an organization and associated changes.""" + i = 0 # [START group_filtered_findings_with_changes] from datetime import timedelta diff --git a/samples/snippets/snippets_findings_test.py b/samples/snippets/snippets_findings_test.py new file mode 100644 index 00000000..7caedc17 --- /dev/null +++ b/samples/snippets/snippets_findings_test.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python +# +# Copyright 2020 Google LLC +# +# 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 +# +# https://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. +import os +from itertools import chain + +import pytest + +import snippets_findings + + +@pytest.fixture(scope="module") +def organization_id(): + """Get Organization ID from the environment variable """ + return os.environ["GCLOUD_ORGANIZATION"] + + +@pytest.fixture(scope="module") +def source_name(organization_id): + from google.cloud import securitycenter + + client = securitycenter.SecurityCenterClient() + org_name = "organizations/{org_id}".format(org_id=organization_id) + + source = client.create_source( + org_name, + { + "display_name": "Unit test source", + "description": "A new custom source that does X", + }, + ) + return source.name + +def test_create_source(organization_id): + snippets_findings.create_source(organization_id) + +def test_get_source(source_name): + source = snippets_findings.get_source(source_name) + assert source.name == source_name + +def test_update_source(source_name): + updated = snippets_findings.test_update_source(source_name) + assert updated.display_name == "Updated Display Name" + +def test_add_user_to_source(source_name): + binding, updated = snippets_findings.test_add_user_to_source(source_name) + assert any( + member == "user:csccclienttest@gmail.com" + for member in chain.from_iterable( + binding.members for binding in updated.bindings + ) + ) + +def test_list_source(organization_id): + count = snippets_findings.list_source(organization_id) + assert count >= 0 + +def test_create_finding(source_name): + created_finding = snippets_findings.create_finding(source_name) + assert len(created_finding.name) > 0 + +def test_create_finding_with_source_properties(source_name): + snippets_findings.test_create_finding_with_source_properties(source_name) + + +def test_update_finding(source_name): + snippets_findings.update_finding(source_name) + +def test_update_finding_state(source_name): + snippets_findings.update_finding_state(source_name) + +def test_trouble_shoot(source_name): + snippets_findings.trouble_shoot(source_name) + +def test_list_all_findings(organization_id): + count = snippets_findings.list_all_findings(organization_id) + assert count > 0 + +def test_list_filtered_findings(source_name): + count = snippets_findings.list_filtered_findings(source_name) + assert count > 0 + +def list_findings_at_time(source_name): + count = snippets_findings.list_findings_at_time(source_name) + assert count == -1 + +def test_get_iam_policy(source_name): + snippets_findings.get_iam_policy(source_name) + +def test_group_all_findings(organization_id): + count = snippets_findings.test_group_all_findings(organization_id) + assert count > 0 + +def test_group_filtered_findings(source_name): + count = snippets_findings.group_filtered_findings(source_name) + assert count == 0 + +def test_group_findings_at_time(source_name): + count = snippets_findings.test_group_findings_at_time(source_name) + assert count == -1 + +def test_group_findings_and_changes(source_name): + count = snippets_findings.test_group_findings_and_changes(source_name) + assert count == 0 diff --git a/samples/snippets/snippets_list_assets.py b/samples/snippets/snippets_list_assets.py index ae50ff91..5c7f1fcd 100644 --- a/samples/snippets/snippets_list_assets.py +++ b/samples/snippets/snippets_list_assets.py @@ -15,19 +15,12 @@ # limitations under the License. """ Examples of listing assets in Cloud Security Command Center.""" -import os -from datetime import datetime, timedelta -import pytest -@pytest.fixture(scope="module") -def organization_id(): - """Get Organization ID from the environment variable """ - return os.environ["GCLOUD_ORGANIZATION"] - -def test_list_all_assets(organization_id): +def list_all_assets(organization_id): """Demonstrate listing and printing all assets.""" + i = 0 # [START demo_list_all_assets] from google.cloud import securitycenter @@ -41,11 +34,12 @@ def test_list_all_assets(organization_id): for i, asset_result in enumerate(asset_iterator): print(i, asset_result) # [END demo_list_all_assets] - assert i > 0 + return i -def test_list_assets_with_filters(organization_id): +def list_assets_with_filters(organization_id): """Demonstrate listing assets with a filter.""" + i = 0 # [START demo_list_assets_with_filter] from google.cloud import securitycenter @@ -64,11 +58,12 @@ def test_list_assets_with_filters(organization_id): for i, asset_result in enumerate(asset_iterator): print(i, asset_result) # [END demo_list_assets_with_filter] - assert i > 0 + return i -def test_list_assets_with_filters_and_read_time(organization_id): +def list_assets_with_filters_and_read_time(organization_id): """Demonstrate listing assets with a filter.""" + i = 0 # [START demo_list_assets_with_filter_and_time] from datetime import datetime, timedelta @@ -99,11 +94,12 @@ def test_list_assets_with_filters_and_read_time(organization_id): for i, asset_result in enumerate(asset_iterator): print(i, asset_result) # [END demo_list_assets_with_filter_and_time] - assert i > 0 + return i -def test_list_point_in_time_changes(organization_id): +def list_point_in_time_changes(organization_id): """Demonstrate listing assets along with their state changes.""" + i = 0 # [START demo_list_assets_changes] from datetime import timedelta @@ -136,8 +132,9 @@ def test_list_point_in_time_changes(organization_id): assert i > 0 -def test_group_assets(organization_id): +def group_assets(organization_id): """Demonstrates grouping all assets by type. """ + i = 0 # [START group_all_assets] from google.cloud import securitycenter @@ -153,12 +150,12 @@ def test_group_assets(organization_id): for i, result in enumerate(result_iterator): print((i + 1), result) # [END group_all_assets] - # 8 different asset types. - assert i >= 8 + return i -def test_group_filtered_assets(organization_id): +def group_filtered_assets(organization_id): """Demonstrates grouping assets by type with a filter. """ + i = 0 # [START group_all_assets] from google.cloud import securitycenter @@ -180,11 +177,12 @@ def test_group_filtered_assets(organization_id): print((i + 1), result) # [END group_all_assets] # only one asset type is a project - assert i == 0 + return i -def test_group_assets_by_changes(organization_id): +def group_assets_by_changes(organization_id): """Demonstrates grouping assets by there changes over a period of time.""" + i = 0 # [START group_all_assets_by_change] from datetime import timedelta @@ -205,5 +203,4 @@ def test_group_assets_by_changes(organization_id): for i, result in enumerate(result_iterator): print((i + 1), result) # [END group_all_assets_by_change] - # only one asset type is a project - assert i >= 0 + return i \ No newline at end of file diff --git a/samples/snippets/snippets_list_assets_test.py b/samples/snippets/snippets_list_assets_test.py new file mode 100644 index 00000000..96e61574 --- /dev/null +++ b/samples/snippets/snippets_list_assets_test.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# +# Copyright 2020 Google LLC +# +# 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 +# +# https://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. +"""Tests for snippets.""" + +import os +import uuid + +import pytest + +import snippets_list_assets + +@pytest.fixture(scope="module") +def organization_id(): + """Get Organization ID from the environment variable """ + return os.environ["GCLOUD_ORGANIZATION"] + +def test_list_all_assets(organization_id): + """Demonstrate listing and printing all assets.""" + count = snippets_list_assets.list_all_assets(organization_id) + assert count > 0 + +def list_assets_with_filters(organization_id): + count = snippets_list_assets.list_all_assets(organization_id) + assert count > 0 + +def test_list_assets_with_filters_and_read_time(organization_id): + count = snippets_list_assets.test_list_assets_with_filters_and_read_time(organization_id) + assert count > 0 + +def test_list_point_in_time_changes(organization_id): + count = snippets_list_assets.test_list_point_in_time_changes(organization_id) + assert count > 0 + +def test_group_assets(organization_id): + count = snippets_list_assets.test_group_assets(organization_id) + assert count >= 8 # 8 different asset types. + +def test_group_filtered_assets(organization_id): + count = snippets_list_assets.test_group_filtered_assets(organization_id) + assert count == 0 + +def test_group_assets_by_changes(organization_id): + count = snippets_list_assets.test_group_assets_by_change(organization_id) + assert count >= 0 # only one asset type is a project \ No newline at end of file diff --git a/samples/snippets/snippets_orgs.py b/samples/snippets/snippets_orgs.py index 7781da11..6b95e49e 100644 --- a/samples/snippets/snippets_orgs.py +++ b/samples/snippets/snippets_orgs.py @@ -14,17 +14,9 @@ # See the License for the specific language governing permissions and # limitations under the License. """Examples for working with organization settings. """ -import os -import pytest -@pytest.fixture(scope="module") -def organization_id(): - """Get Organization ID from the environment variable """ - return os.environ["GCLOUD_ORGANIZATION"] - - -def test_get_settings(organization_id): +def get_settings(organization_id): """Example showing how to retreive current organization settings.""" # [START get_org_settings] from google.cloud import securitycenter @@ -40,7 +32,7 @@ def test_get_settings(organization_id): # [END get_org_settings] -def test_update_asset_discovery_org_settings(organization_id): +def update_asset_discovery_org_settings(organization_id): """Example showing how to update the asset discovery configuration for an organization.""" # [START update_org_settings] @@ -63,4 +55,4 @@ def test_update_asset_discovery_org_settings(organization_id): ) print("Asset Discovery Enabled? {}".format(updated.enable_asset_discovery)) # [END update_org_settings] - assert updated.enable_asset_discovery == True + return updated diff --git a/samples/snippets/snippets_orgs_test.py b/samples/snippets/snippets_orgs_test.py new file mode 100644 index 00000000..f6ea8a38 --- /dev/null +++ b/samples/snippets/snippets_orgs_test.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# +# Copyright 2019 Google LLC +# +# 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 +# +# https://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. +"""Examples for working with organization settings. """ +import os +import pytest + +import snippets_orgs + +@pytest.fixture(scope="module") +def organization_id(): + """Get Organization ID from the environment variable """ + return os.environ["GCLOUD_ORGANIZATION"] + +def test_get_settings(organization_id): + snippets_orgs.get_settings(organization_id) + +def test_update_asset_discovery_org_settings(organization_id): + updated = snippets_orgs.update_asset_discovery_org_settings(organization_id) + assert updated.enable_asset_discovery == True \ No newline at end of file diff --git a/samples/snippets/snippets_security_marks_test.py b/samples/snippets/snippets_security_marks_test.py new file mode 100644 index 00000000..cd96b335 --- /dev/null +++ b/samples/snippets/snippets_security_marks_test.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python +# +# Copyright 2019 Google LLC +# +# 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 +# +# https://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. +"""Demos for working with security marks.""" +import os +import random + +import pytest + +import snippets_security_marks + +@pytest.fixture(scope="module") +def organization_id(): + """Gets Organization ID from the environment variable """ + return os.environ["GCLOUD_ORGANIZATION"] + + +@pytest.fixture(scope="module") +def asset_name(organization_id): + """Returns a random asset name from existing assets.""" + from google.cloud import securitycenter + + client = securitycenter.SecurityCenterClient() + # organization_id is the numeric ID of the organization. + # organization_id=1234567777 + org_name = "organizations/{org_id}".format(org_id=organization_id) + assets = list(client.list_assets(org_name)) + # Select a random asset to avoid collision between integration tests. + asset = (random.sample(assets, 1)[0]).asset.name + + # Set fresh marks. + update = client.update_security_marks( + {"name": "{}/securityMarks".format(asset), "marks": {"other": "other_val"}} + ) + assert update.marks == {"other": "other_val"} + return asset + + +@pytest.fixture(scope="module") +def source_name(organization_id): + """Creates a new source in the organization.""" + from google.cloud import securitycenter + + client = securitycenter.SecurityCenterClient() + org_name = "organizations/{org_id}".format(org_id=organization_id) + source = client.create_source( + org_name, + { + "display_name": "Security marks Unit test source", + "description": "A new custom source that does X", + }, + ) + return source.name + +@pytest.fixture(scope="module") +def finding_name(source_name): + """Creates a new finding and returns it name.""" + from google.cloud import securitycenter + from google.cloud.securitycenter_v1.proto.finding_pb2 import Finding + from google.protobuf.timestamp_pb2 import Timestamp + + client = securitycenter.SecurityCenterClient() + + now_proto = Timestamp() + now_proto.GetCurrentTime() + + finding = client.create_finding( + source_name, + "scfinding", + { + "state": Finding.ACTIVE, + "category": "C1", + "event_time": now_proto, + "resource_name": "//cloudresourcemanager.googleapis.com/organizations/1234", + }, + ) + client.create_finding( + source_name, + "untouched", + { + "state": Finding.ACTIVE, + "category": "MEDIUM_RISK_ONE", + "event_time": now_proto, + "resource_name": "//cloudresourcemanager.googleapis.com/organizations/1234", + }, + ) + + return finding.name + +def test_add_to_asset(asset_name): + updated_marks, marks = snippets_security_marks.add_to_asset(asset_name) + assert updated_marks.marks.keys() >= marks.keys() + +def test_clear_from_asset(asset_name): + updated_marks = snippets_security_marks.clear_from_asset(asset_name) + assert "other" in updated_marks.marks + assert len(updated_marks.marks) == 1 + +def test_delete_and_update_marks(asset_name): + updated_marks = snippets_security_marks.delete_and_update_marks(asset_name) + assert updated_marks.marks == {"key_a": "new_value_for_a", "other": "other_val"} + +def test_add_to_finding(finding_name): + updated_marks, marks = snippets_security_marks.add_to_finding(finding_name) + assert updated_marks.marks == marks + +def test_list_assets_with_query_marks(organization_id, asset_name): + count = snippets_security_marks.list_assets_with_query_marks(organization_id, asset_name) + assert count >= 0 + +def test_list_findings_with_query_marks(source_name, finding_name): + count = snippets_security_marks.list_findings_with_query_marks(source_name, finding_name) + assert count == 0 \ No newline at end of file From 6ff231f922505529d8a67d643cd0a89a85ddd23c Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Fri, 10 Jul 2020 21:56:52 +0000 Subject: [PATCH 4/9] test: fix function names --- samples/snippets/snippets_findings_test.py | 12 +++++------ samples/snippets/snippets_list_assets_test.py | 10 +++++----- samples/snippets/snippets_security_marks.py | 20 +++++++++---------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/samples/snippets/snippets_findings_test.py b/samples/snippets/snippets_findings_test.py index 7caedc17..b5e0df90 100644 --- a/samples/snippets/snippets_findings_test.py +++ b/samples/snippets/snippets_findings_test.py @@ -51,11 +51,11 @@ def test_get_source(source_name): assert source.name == source_name def test_update_source(source_name): - updated = snippets_findings.test_update_source(source_name) + updated = snippets_findings.update_source(source_name) assert updated.display_name == "Updated Display Name" def test_add_user_to_source(source_name): - binding, updated = snippets_findings.test_add_user_to_source(source_name) + binding, updated = snippets_findings.add_user_to_source(source_name) assert any( member == "user:csccclienttest@gmail.com" for member in chain.from_iterable( @@ -72,7 +72,7 @@ def test_create_finding(source_name): assert len(created_finding.name) > 0 def test_create_finding_with_source_properties(source_name): - snippets_findings.test_create_finding_with_source_properties(source_name) + snippets_findings.create_finding_with_source_properties(source_name) def test_update_finding(source_name): @@ -100,7 +100,7 @@ def test_get_iam_policy(source_name): snippets_findings.get_iam_policy(source_name) def test_group_all_findings(organization_id): - count = snippets_findings.test_group_all_findings(organization_id) + count = snippets_findings.group_all_findings(organization_id) assert count > 0 def test_group_filtered_findings(source_name): @@ -108,9 +108,9 @@ def test_group_filtered_findings(source_name): assert count == 0 def test_group_findings_at_time(source_name): - count = snippets_findings.test_group_findings_at_time(source_name) + count = snippets_findings.group_findings_at_time(source_name) assert count == -1 def test_group_findings_and_changes(source_name): - count = snippets_findings.test_group_findings_and_changes(source_name) + count = snippets_findings.group_findings_and_changes(source_name) assert count == 0 diff --git a/samples/snippets/snippets_list_assets_test.py b/samples/snippets/snippets_list_assets_test.py index 96e61574..af96125e 100644 --- a/samples/snippets/snippets_list_assets_test.py +++ b/samples/snippets/snippets_list_assets_test.py @@ -37,21 +37,21 @@ def list_assets_with_filters(organization_id): assert count > 0 def test_list_assets_with_filters_and_read_time(organization_id): - count = snippets_list_assets.test_list_assets_with_filters_and_read_time(organization_id) + count = snippets_list_assets.list_assets_with_filters_and_read_time(organization_id) assert count > 0 def test_list_point_in_time_changes(organization_id): - count = snippets_list_assets.test_list_point_in_time_changes(organization_id) + count = snippets_list_assets.list_point_in_time_changes(organization_id) assert count > 0 def test_group_assets(organization_id): - count = snippets_list_assets.test_group_assets(organization_id) + count = snippets_list_assets.group_assets(organization_id) assert count >= 8 # 8 different asset types. def test_group_filtered_assets(organization_id): - count = snippets_list_assets.test_group_filtered_assets(organization_id) + count = snippets_list_assets.group_filtered_assets(organization_id) assert count == 0 def test_group_assets_by_changes(organization_id): - count = snippets_list_assets.test_group_assets_by_change(organization_id) + count = snippets_list_assets.group_assets_by_change(organization_id) assert count >= 0 # only one asset type is a project \ No newline at end of file diff --git a/samples/snippets/snippets_security_marks.py b/samples/snippets/snippets_security_marks.py index ef2f5d73..76cccd53 100644 --- a/samples/snippets/snippets_security_marks.py +++ b/samples/snippets/snippets_security_marks.py @@ -100,7 +100,7 @@ def finding_name(source_name): return finding.name -def test_add_to_asset(asset_name): +def add_to_asset(asset_name): """Add new security marks to an asset.""" # [START add_marks_to_asset] from google.cloud import securitycenter @@ -131,10 +131,10 @@ def test_add_to_asset(asset_name): assert updated_marks.marks.keys() >= marks.keys() -def test_clear_from_asset(asset_name): +def clear_from_asset(asset_name): """Removes security marks from an asset.""" # Make sure they are there first - test_add_to_asset(asset_name) + add_to_asset(asset_name) # [START clear_marks_asset] from google.cloud import securitycenter from google.protobuf import field_mask_pb2 @@ -165,10 +165,10 @@ def test_clear_from_asset(asset_name): assert len(updated_marks.marks) == 1 -def test_delete_and_update_marks(asset_name): +def delete_and_update_marks(asset_name): """Updates and deletes security marks from an asset in the same call.""" # Make sure they are there first - test_add_to_asset(asset_name) + add_to_asset(asset_name) # [START delete_and_update_marks] from google.cloud import securitycenter from google.protobuf import field_mask_pb2 @@ -191,7 +191,7 @@ def test_delete_and_update_marks(asset_name): assert updated_marks.marks == {"key_a": "new_value_for_a", "other": "other_val"} -def test_add_to_finding(finding_name): +def add_to_finding(finding_name): """Adds security marks to a finding. """ # [START add_marks_to_finding] from google.cloud import securitycenter @@ -220,9 +220,9 @@ def test_add_to_finding(finding_name): assert updated_marks.marks == marks -def test_list_assets_with_query_marks(organization_id, asset_name): +def list_assets_with_query_marks(organization_id, asset_name): """Lists assets with a filter on security marks. """ - test_add_to_asset(asset_name) + add_to_asset(asset_name) # [START demo_list_assets_with_security_marks] from google.cloud import securitycenter @@ -244,10 +244,10 @@ def test_list_assets_with_query_marks(organization_id, asset_name): assert i >= 0 -def test_list_findings_with_query_marks(source_name, finding_name): +def list_findings_with_query_marks(source_name, finding_name): """Lists findings with a filter on security marks.""" # ensure marks are set on finding. - test_add_to_finding(finding_name) + add_to_finding(finding_name) i = -1 # [START demo_list_findings_with_security_marks] from google.cloud import securitycenter From 4fc291483c6d9d7a702a8a186882088df26385d7 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Fri, 10 Jul 2020 23:07:27 +0000 Subject: [PATCH 5/9] chore: fix lint --- samples/snippets/noxfile.py | 26 +++--- samples/snippets/noxfile_config.py | 16 ++-- samples/snippets/snippets_findings.py | 14 ++- samples/snippets/snippets_findings_test.py | 19 +++- samples/snippets/snippets_list_assets.py | 5 +- samples/snippets/snippets_list_assets_test.py | 13 ++- .../snippets/snippets_notification_configs.py | 2 +- .../snippets_notification_receiver.py | 3 +- .../snippets/snippets_notification_test.py | 8 +- samples/snippets/snippets_orgs_test.py | 6 +- samples/snippets/snippets_security_marks.py | 86 +------------------ .../snippets/snippets_security_marks_test.py | 18 +++- 12 files changed, 81 insertions(+), 135 deletions(-) diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index ba55d7ce..5660f08b 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -37,24 +37,22 @@ TEST_CONFIG = { # You can opt out from the test for specific Python versions. - 'ignored_versions': ["2.7"], - + "ignored_versions": ["2.7"], # An envvar key for determining the project id to use. Change it # to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a # build specific Cloud project. You can also use your own string # to use your own Cloud project. - 'gcloud_project_env': 'GOOGLE_CLOUD_PROJECT', + "gcloud_project_env": "GOOGLE_CLOUD_PROJECT", # 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT', - # A dictionary you want to inject into your test. Don't put any # secrets here. These values will override predefined values. - 'envs': {}, + "envs": {}, } try: # Ensure we can import noxfile_config in the project's directory. - sys.path.append('.') + sys.path.append(".") from noxfile_config import TEST_CONFIG_OVERRIDE except ImportError as e: print("No user noxfile_config found: detail: {}".format(e)) @@ -69,12 +67,12 @@ def get_pytest_env_vars(): ret = {} # Override the GCLOUD_PROJECT and the alias. - env_key = TEST_CONFIG['gcloud_project_env'] + env_key = TEST_CONFIG["gcloud_project_env"] # This should error out if not set. - ret['GOOGLE_CLOUD_PROJECT'] = os.environ[env_key] + ret["GOOGLE_CLOUD_PROJECT"] = os.environ[env_key] # Apply user supplied envs. - ret.update(TEST_CONFIG['envs']) + ret.update(TEST_CONFIG["envs"]) return ret @@ -83,7 +81,7 @@ def get_pytest_env_vars(): ALL_VERSIONS = ["2.7", "3.6", "3.7", "3.8"] # Any default versions that should be ignored. -IGNORED_VERSIONS = TEST_CONFIG['ignored_versions'] +IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] TESTED_VERSIONS = sorted([v for v in ALL_VERSIONS if v not in IGNORED_VERSIONS]) @@ -138,7 +136,7 @@ def lint(session): args = FLAKE8_COMMON_ARGS + [ "--application-import-names", ",".join(local_names), - "." + ".", ] session.run("flake8", *args) @@ -182,9 +180,9 @@ def py(session): if session.python in TESTED_VERSIONS: _session_tests(session) else: - session.skip("SKIPPED: {} tests are disabled for this sample.".format( - session.python - )) + session.skip( + "SKIPPED: {} tests are disabled for this sample.".format(session.python) + ) # diff --git a/samples/snippets/noxfile_config.py b/samples/snippets/noxfile_config.py index cb62b824..d8475162 100644 --- a/samples/snippets/noxfile_config.py +++ b/samples/snippets/noxfile_config.py @@ -22,20 +22,18 @@ TEST_CONFIG_OVERRIDE = { # You can opt out from the test for specific Python versions. - 'ignored_versions': ["2.7"], - + "ignored_versions": ["2.7"], # An envvar key for determining the project id to use. Change it # to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a # build specific Cloud project. You can also use your own string # to use your own Cloud project. # 'gcloud_project_env': 'GOOGLE_CLOUD_PROJECT', - # A dictionary you want to inject into your test. Don't put any # secrets here. These values will override predefined values. - 'envs': { - 'GCLOUD_ORGANIZATION': '1081635000895', - 'GCLOUD_PROJECT': 'project-a-id', - 'GCLOUD_PUBSUB_TOPIC': 'projects/project-a-id/topics/notifications-sample-topic', - 'GCLOUD_PUBSUB_SUBSCRIPTION': 'notification_sample_subscription', + "envs": { + "GCLOUD_ORGANIZATION": "1081635000895", + "GCLOUD_PROJECT": "project-a-id", + "GCLOUD_PUBSUB_TOPIC": "projects/project-a-id/topics/notifications-sample-topic", + "GCLOUD_PUBSUB_SUBSCRIPTION": "notification_sample_subscription", }, -} \ No newline at end of file +} diff --git a/samples/snippets/snippets_findings.py b/samples/snippets/snippets_findings.py index 5a5cb8f3..ec465f20 100644 --- a/samples/snippets/snippets_findings.py +++ b/samples/snippets/snippets_findings.py @@ -17,7 +17,6 @@ """Examples of working with source and findings in Cloud Security Command Center.""" - def create_source(organization_id): """Create a new findings source. """ # [START create_source] @@ -122,7 +121,6 @@ def add_user_to_source(source_name): return binding, updated - def list_source(organization_id): """Lists finding sources.""" i = -1 @@ -139,7 +137,7 @@ def list_source(organization_id): for i, source in enumerate(client.list_sources(org_name)): print(i, source) # [END list_sources] - + return i def create_finding(source_name): @@ -289,7 +287,6 @@ def update_finding_state(source_name): from google.cloud import securitycenter from google.cloud.securitycenter_v1.proto.finding_pb2 import Finding from google.protobuf.timestamp_pb2 import Timestamp - from datetime import datetime # Create a client. client = securitycenter.SecurityCenterClient() @@ -445,7 +442,6 @@ def get_iam_policy(source_name): """Gives a user findingsEditor permission to the source.""" # [START get_source_iam] from google.cloud import securitycenter - from google.iam.v1 import policy_pb2 client = securitycenter.SecurityCenterClient() @@ -480,7 +476,7 @@ def group_all_findings(organization_id): for i, group_result in enumerate(group_result_iterator): print((i + 1), group_result) # [END group_all_findings] - assert i > 0 + return i def group_filtered_findings(source_name): @@ -505,7 +501,7 @@ def group_filtered_findings(source_name): for i, group_result in enumerate(group_result_iterator): print((i + 1), group_result) # [END group_filtered_findings] - assert i == 0 + return i def group_findings_at_time(source_name): @@ -538,7 +534,7 @@ def group_findings_at_time(source_name): for i, group_result in enumerate(group_result_iterator): print((i + 1), group_result) # [END group_filtered_findings_at_time] - assert i == -1 + return i def group_findings_and_changes(source_name): @@ -573,4 +569,4 @@ def group_findings_and_changes(source_name): for i, group_result in enumerate(group_result_iterator): print((i + 1), group_result) # [END group_findings_with_changes] - assert i == 0 + return i diff --git a/samples/snippets/snippets_findings_test.py b/samples/snippets/snippets_findings_test.py index b5e0df90..8ac01d8c 100644 --- a/samples/snippets/snippets_findings_test.py +++ b/samples/snippets/snippets_findings_test.py @@ -13,8 +13,8 @@ # 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. -import os from itertools import chain +import os import pytest @@ -43,17 +43,21 @@ def source_name(organization_id): ) return source.name + def test_create_source(organization_id): snippets_findings.create_source(organization_id) + def test_get_source(source_name): source = snippets_findings.get_source(source_name) assert source.name == source_name + def test_update_source(source_name): updated = snippets_findings.update_source(source_name) assert updated.display_name == "Updated Display Name" + def test_add_user_to_source(source_name): binding, updated = snippets_findings.add_user_to_source(source_name) assert any( @@ -63,14 +67,17 @@ def test_add_user_to_source(source_name): ) ) + def test_list_source(organization_id): count = snippets_findings.list_source(organization_id) assert count >= 0 + def test_create_finding(source_name): created_finding = snippets_findings.create_finding(source_name) assert len(created_finding.name) > 0 + def test_create_finding_with_source_properties(source_name): snippets_findings.create_finding_with_source_properties(source_name) @@ -78,39 +85,49 @@ def test_create_finding_with_source_properties(source_name): def test_update_finding(source_name): snippets_findings.update_finding(source_name) + def test_update_finding_state(source_name): snippets_findings.update_finding_state(source_name) + def test_trouble_shoot(source_name): snippets_findings.trouble_shoot(source_name) + def test_list_all_findings(organization_id): count = snippets_findings.list_all_findings(organization_id) assert count > 0 + def test_list_filtered_findings(source_name): count = snippets_findings.list_filtered_findings(source_name) assert count > 0 + def list_findings_at_time(source_name): count = snippets_findings.list_findings_at_time(source_name) assert count == -1 + def test_get_iam_policy(source_name): snippets_findings.get_iam_policy(source_name) + def test_group_all_findings(organization_id): count = snippets_findings.group_all_findings(organization_id) assert count > 0 + def test_group_filtered_findings(source_name): count = snippets_findings.group_filtered_findings(source_name) assert count == 0 + def test_group_findings_at_time(source_name): count = snippets_findings.group_findings_at_time(source_name) assert count == -1 + def test_group_findings_and_changes(source_name): count = snippets_findings.group_findings_and_changes(source_name) assert count == 0 diff --git a/samples/snippets/snippets_list_assets.py b/samples/snippets/snippets_list_assets.py index 5c7f1fcd..f8863802 100644 --- a/samples/snippets/snippets_list_assets.py +++ b/samples/snippets/snippets_list_assets.py @@ -17,7 +17,6 @@ """ Examples of listing assets in Cloud Security Command Center.""" - def list_all_assets(organization_id): """Demonstrate listing and printing all assets.""" i = 0 @@ -129,7 +128,7 @@ def list_point_in_time_changes(organization_id): print(i, asset) # [END demo_list_assets_changes] - assert i > 0 + return i def group_assets(organization_id): @@ -203,4 +202,4 @@ def group_assets_by_changes(organization_id): for i, result in enumerate(result_iterator): print((i + 1), result) # [END group_all_assets_by_change] - return i \ No newline at end of file + return i diff --git a/samples/snippets/snippets_list_assets_test.py b/samples/snippets/snippets_list_assets_test.py index af96125e..a2646844 100644 --- a/samples/snippets/snippets_list_assets_test.py +++ b/samples/snippets/snippets_list_assets_test.py @@ -16,42 +16,49 @@ """Tests for snippets.""" import os -import uuid import pytest import snippets_list_assets + @pytest.fixture(scope="module") def organization_id(): """Get Organization ID from the environment variable """ return os.environ["GCLOUD_ORGANIZATION"] + def test_list_all_assets(organization_id): """Demonstrate listing and printing all assets.""" count = snippets_list_assets.list_all_assets(organization_id) assert count > 0 + def list_assets_with_filters(organization_id): count = snippets_list_assets.list_all_assets(organization_id) assert count > 0 + def test_list_assets_with_filters_and_read_time(organization_id): count = snippets_list_assets.list_assets_with_filters_and_read_time(organization_id) assert count > 0 + def test_list_point_in_time_changes(organization_id): count = snippets_list_assets.list_point_in_time_changes(organization_id) assert count > 0 + def test_group_assets(organization_id): count = snippets_list_assets.group_assets(organization_id) assert count >= 8 # 8 different asset types. + def test_group_filtered_assets(organization_id): count = snippets_list_assets.group_filtered_assets(organization_id) assert count == 0 + def test_group_assets_by_changes(organization_id): - count = snippets_list_assets.group_assets_by_change(organization_id) - assert count >= 0 # only one asset type is a project \ No newline at end of file + count = snippets_list_assets.group_assets_by_changes(organization_id) + assert count >= 0 # only one asset type is a project diff --git a/samples/snippets/snippets_notification_configs.py b/samples/snippets/snippets_notification_configs.py index 42d1f70f..acc4b8ba 100644 --- a/samples/snippets/snippets_notification_configs.py +++ b/samples/snippets/snippets_notification_configs.py @@ -36,7 +36,7 @@ def create_notification_config(organization_id, notification_config_id, pubsub_t { "description": "Notification for active findings", "pubsub_topic": pubsub_topic, - "streaming_config": {"filter": 'state = "ACTIVE"',}, + "streaming_config": {"filter": 'state = "ACTIVE"'}, }, ) diff --git a/samples/snippets/snippets_notification_receiver.py b/samples/snippets/snippets_notification_receiver.py index fd2fdcae..e1e6608f 100644 --- a/samples/snippets/snippets_notification_receiver.py +++ b/samples/snippets/snippets_notification_receiver.py @@ -19,6 +19,7 @@ def receive_notifications(project_id, subscription_name): # [START scc_receive_notifications] # Requires https://cloud.google.com/pubsub/docs/quickstart-client-libraries#pubsub-client-libraries-python + import google.api_core.exceptions.DeadlineExceeded from google.cloud import pubsub_v1 from google.cloud.securitycenter_v1.proto.notification_message_pb2 import ( NotificationMessage, @@ -52,7 +53,7 @@ def callback(message): print("Listening for messages on {}...\n".format(subscription_path)) try: streaming_pull_future.result(timeout=1) # Block for 1 second - except: + except google.api_core.exceptions.DeadlineExceeded: streaming_pull_future.cancel() # [END scc_receive_notifications] return True diff --git a/samples/snippets/snippets_notification_test.py b/samples/snippets/snippets_notification_test.py index d24d0906..73ad0060 100644 --- a/samples/snippets/snippets_notification_test.py +++ b/samples/snippets/snippets_notification_test.py @@ -56,7 +56,7 @@ def new_notification_config_for_update(): { "description": "Notification for active findings", "pubsub_topic": PUBSUB_TOPIC, - "streaming_config": {"filter": "",}, + "streaming_config": {"filter": ""}, }, ) yield created_notification_config @@ -75,7 +75,7 @@ def new_notification_config_for_get(): { "description": "Notification for active findings", "pubsub_topic": PUBSUB_TOPIC, - "streaming_config": {"filter": "",}, + "streaming_config": {"filter": ""}, }, ) yield created_notification_config @@ -94,7 +94,7 @@ def deleted_notification_config(): { "description": "Notification for active findings", "pubsub_topic": PUBSUB_TOPIC, - "streaming_config": {"filter": "",}, + "streaming_config": {"filter": ""}, }, ) return created_notification_config @@ -114,7 +114,6 @@ def test_delete_notification_config(deleted_notification_config): snippets_notification_configs.delete_notification_config( ORG_ID, DELETE_CONFIG_ID ) - == True ) @@ -142,5 +141,4 @@ def test_receive_notifications(): snippets_notification_receiver.receive_notifications( PROJECT_ID, PUBSUB_SUBSCRIPTION ) - == True ) diff --git a/samples/snippets/snippets_orgs_test.py b/samples/snippets/snippets_orgs_test.py index f6ea8a38..fc9a3a90 100644 --- a/samples/snippets/snippets_orgs_test.py +++ b/samples/snippets/snippets_orgs_test.py @@ -15,18 +15,22 @@ # limitations under the License. """Examples for working with organization settings. """ import os + import pytest import snippets_orgs + @pytest.fixture(scope="module") def organization_id(): """Get Organization ID from the environment variable """ return os.environ["GCLOUD_ORGANIZATION"] + def test_get_settings(organization_id): snippets_orgs.get_settings(organization_id) + def test_update_asset_discovery_org_settings(organization_id): updated = snippets_orgs.update_asset_discovery_org_settings(organization_id) - assert updated.enable_asset_discovery == True \ No newline at end of file + assert updated.enable_asset_discovery diff --git a/samples/snippets/snippets_security_marks.py b/samples/snippets/snippets_security_marks.py index 76cccd53..8cea9094 100644 --- a/samples/snippets/snippets_security_marks.py +++ b/samples/snippets/snippets_security_marks.py @@ -14,90 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. """Demos for working with security marks.""" -import os -import random - -import pytest - - -@pytest.fixture(scope="module") -def organization_id(): - """Gets Organization ID from the environment variable """ - return os.environ["GCLOUD_ORGANIZATION"] - - -@pytest.fixture(scope="module") -def asset_name(organization_id): - """Returns a random asset name from existing assets.""" - from google.cloud import securitycenter - - client = securitycenter.SecurityCenterClient() - # organization_id is the numeric ID of the organization. - # organization_id=1234567777 - org_name = "organizations/{org_id}".format(org_id=organization_id) - assets = list(client.list_assets(org_name)) - # Select a random asset to avoid collision between integration tests. - asset = (random.sample(assets, 1)[0]).asset.name - - # Set fresh marks. - update = client.update_security_marks( - {"name": "{}/securityMarks".format(asset), "marks": {"other": "other_val"}} - ) - assert update.marks == {"other": "other_val"} - return asset - - -@pytest.fixture(scope="module") -def source_name(organization_id): - """Creates a new source in the organization.""" - from google.cloud import securitycenter - - client = securitycenter.SecurityCenterClient() - org_name = "organizations/{org_id}".format(org_id=organization_id) - source = client.create_source( - org_name, - { - "display_name": "Security marks Unit test source", - "description": "A new custom source that does X", - }, - ) - return source.name - - -@pytest.fixture(scope="module") -def finding_name(source_name): - """Creates a new finding and returns it name.""" - from google.cloud import securitycenter - from google.cloud.securitycenter_v1.proto.finding_pb2 import Finding - from google.protobuf.timestamp_pb2 import Timestamp - - client = securitycenter.SecurityCenterClient() - - now_proto = Timestamp() - now_proto.GetCurrentTime() - - finding = client.create_finding( - source_name, - "scfinding", - { - "state": Finding.ACTIVE, - "category": "C1", - "event_time": now_proto, - "resource_name": "//cloudresourcemanager.googleapis.com/organizations/1234", - }, - ) - client.create_finding( - source_name, - "untouched", - { - "state": Finding.ACTIVE, - "category": "MEDIUM_RISK_ONE", - "event_time": now_proto, - "resource_name": "//cloudresourcemanager.googleapis.com/organizations/1234", - }, - ) - - return finding.name def add_to_asset(asset_name): @@ -128,7 +44,7 @@ def add_to_asset(asset_name): ) print(updated_marks) # [END add_marks_to_asset] - assert updated_marks.marks.keys() >= marks.keys() + return updated_marks, marks def clear_from_asset(asset_name): diff --git a/samples/snippets/snippets_security_marks_test.py b/samples/snippets/snippets_security_marks_test.py index cd96b335..18950f86 100644 --- a/samples/snippets/snippets_security_marks_test.py +++ b/samples/snippets/snippets_security_marks_test.py @@ -21,6 +21,7 @@ import snippets_security_marks + @pytest.fixture(scope="module") def organization_id(): """Gets Organization ID from the environment variable """ @@ -64,6 +65,7 @@ def source_name(organization_id): ) return source.name + @pytest.fixture(scope="module") def finding_name(source_name): """Creates a new finding and returns it name.""" @@ -99,27 +101,37 @@ def finding_name(source_name): return finding.name + def test_add_to_asset(asset_name): updated_marks, marks = snippets_security_marks.add_to_asset(asset_name) assert updated_marks.marks.keys() >= marks.keys() + def test_clear_from_asset(asset_name): updated_marks = snippets_security_marks.clear_from_asset(asset_name) assert "other" in updated_marks.marks assert len(updated_marks.marks) == 1 + def test_delete_and_update_marks(asset_name): updated_marks = snippets_security_marks.delete_and_update_marks(asset_name) assert updated_marks.marks == {"key_a": "new_value_for_a", "other": "other_val"} + def test_add_to_finding(finding_name): updated_marks, marks = snippets_security_marks.add_to_finding(finding_name) assert updated_marks.marks == marks + def test_list_assets_with_query_marks(organization_id, asset_name): - count = snippets_security_marks.list_assets_with_query_marks(organization_id, asset_name) + count = snippets_security_marks.list_assets_with_query_marks( + organization_id, asset_name + ) assert count >= 0 + def test_list_findings_with_query_marks(source_name, finding_name): - count = snippets_security_marks.list_findings_with_query_marks(source_name, finding_name) - assert count == 0 \ No newline at end of file + count = snippets_security_marks.list_findings_with_query_marks( + source_name, finding_name + ) + assert count == 0 From 65ff9f9017dde80ab50fef4efe8a0524467d9274 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Fri, 10 Jul 2020 23:18:03 +0000 Subject: [PATCH 6/9] test: fix more tests --- samples/snippets/snippets_notification_receiver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/snippets_notification_receiver.py b/samples/snippets/snippets_notification_receiver.py index e1e6608f..ae60fa17 100644 --- a/samples/snippets/snippets_notification_receiver.py +++ b/samples/snippets/snippets_notification_receiver.py @@ -19,7 +19,7 @@ def receive_notifications(project_id, subscription_name): # [START scc_receive_notifications] # Requires https://cloud.google.com/pubsub/docs/quickstart-client-libraries#pubsub-client-libraries-python - import google.api_core.exceptions.DeadlineExceeded + import google.api_core.exceptions from google.cloud import pubsub_v1 from google.cloud.securitycenter_v1.proto.notification_message_pb2 import ( NotificationMessage, From a4b7c51b15614cd835ccafefb2965923fbd8ef15 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Fri, 10 Jul 2020 23:32:24 +0000 Subject: [PATCH 7/9] test: more fixes --- samples/snippets/snippets_notification_receiver.py | 5 +++-- samples/snippets/snippets_security_marks.py | 13 ++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/samples/snippets/snippets_notification_receiver.py b/samples/snippets/snippets_notification_receiver.py index ae60fa17..aad6ba79 100644 --- a/samples/snippets/snippets_notification_receiver.py +++ b/samples/snippets/snippets_notification_receiver.py @@ -19,7 +19,8 @@ def receive_notifications(project_id, subscription_name): # [START scc_receive_notifications] # Requires https://cloud.google.com/pubsub/docs/quickstart-client-libraries#pubsub-client-libraries-python - import google.api_core.exceptions + import concurrent + from google.cloud import pubsub_v1 from google.cloud.securitycenter_v1.proto.notification_message_pb2 import ( NotificationMessage, @@ -53,7 +54,7 @@ def callback(message): print("Listening for messages on {}...\n".format(subscription_path)) try: streaming_pull_future.result(timeout=1) # Block for 1 second - except google.api_core.exceptions.DeadlineExceeded: + except concurrent.futures.TimeoutError: streaming_pull_future.cancel() # [END scc_receive_notifications] return True diff --git a/samples/snippets/snippets_security_marks.py b/samples/snippets/snippets_security_marks.py index 8cea9094..88532341 100644 --- a/samples/snippets/snippets_security_marks.py +++ b/samples/snippets/snippets_security_marks.py @@ -77,8 +77,7 @@ def clear_from_asset(asset_name): ) print(updated_marks) # [END clear_marks_asset] - assert "other" in updated_marks.marks - assert len(updated_marks.marks) == 1 + return updated_marks def delete_and_update_marks(asset_name): @@ -104,7 +103,7 @@ def delete_and_update_marks(asset_name): ) print(updated_marks) # [END delete_and_update_marks] - assert updated_marks.marks == {"key_a": "new_value_for_a", "other": "other_val"} + return updated_marks def add_to_finding(finding_name): @@ -132,13 +131,13 @@ def add_to_finding(finding_name): {"name": finding_marks_name, "marks": marks}, update_mask=field_mask ) # [END add_marks_to_finding] - - assert updated_marks.marks == marks + return updated_marks, marks def list_assets_with_query_marks(organization_id, asset_name): """Lists assets with a filter on security marks. """ add_to_asset(asset_name) + i = -1 # [START demo_list_assets_with_security_marks] from google.cloud import securitycenter @@ -157,7 +156,7 @@ def list_assets_with_query_marks(organization_id, asset_name): for i, asset_result in enumerate(asset_iterator): print(i, asset_result) # [END demo_list_assets_with_security_marks] - assert i >= 0 + return i def list_findings_with_query_marks(source_name, finding_name): @@ -185,4 +184,4 @@ def list_findings_with_query_marks(source_name, finding_name): # [END demo_list_findings_with_security_marks] # one finding should have been updated with keys, and one should be # untouched. - assert i == 0 + return i From 5fe9e4a4bd22b9e5ae90b33133d2b0ada7480cc0 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Fri, 10 Jul 2020 23:59:29 +0000 Subject: [PATCH 8/9] chore: add codeowners --- .github/CODEOWNERS | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..3b4e6804 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,8 @@ +# Code owners file. +# This file controls who is tagged for review for any given pull request. +# +# For syntax help see: +# https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax + + +/samples/ @tdh911 @googleapis/python-samples-owners \ No newline at end of file From 65318e70c3a95b2a9dfde6acf774d71ef51af7d6 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Date: Mon, 13 Jul 2020 20:12:11 -0700 Subject: [PATCH 9/9] test: fix pubsub subscription name --- samples/snippets/noxfile_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/noxfile_config.py b/samples/snippets/noxfile_config.py index d8475162..8e6ed132 100644 --- a/samples/snippets/noxfile_config.py +++ b/samples/snippets/noxfile_config.py @@ -34,6 +34,6 @@ "GCLOUD_ORGANIZATION": "1081635000895", "GCLOUD_PROJECT": "project-a-id", "GCLOUD_PUBSUB_TOPIC": "projects/project-a-id/topics/notifications-sample-topic", - "GCLOUD_PUBSUB_SUBSCRIPTION": "notification_sample_subscription", + "GCLOUD_PUBSUB_SUBSCRIPTION": "notification-sample-subscription", }, }