Skip to content

Commit 7ea122e

Browse files
vijaykanthmglasnt
andauthored
feat(securitycenter): Add Resource SCC Org Mgt API ETD Custom Modules (GetEff, ListEff, ListDesc, Validate) (#13081)
* feat(securitycenter): Add Resource SCC Mgt API Org ETD Cust Modules (GetEff, ListEff, ListDesc, Validate) * fix lint error and add nox file * SKIP: only test latest due to concurrency issues * remove unnecessary files --------- Co-authored-by: Katie McLaughlin <[email protected]>
1 parent 29cca06 commit 7ea122e

File tree

3 files changed

+234
-5
lines changed

3 files changed

+234
-5
lines changed

securitycenter/snippets_management_api/event_threat_detection_custom_modules.py

Lines changed: 172 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python
22
#
3-
# Copyright 2024 Google LLC
3+
# Copyright 2025 Google LLC
44
#
55
# Licensed under the Apache License, Version 2.0 (the "License");
66
# you may not use this file except in compliance with the License.
@@ -16,7 +16,7 @@
1616

1717
import uuid
1818

19-
from google.api_core.exceptions import GoogleAPICallError, NotFound
19+
from google.api_core.exceptions import GoogleAPICallError, NotFound, RetryError
2020
from google.cloud import securitycentermanagement_v1
2121
from google.protobuf.field_mask_pb2 import FieldMask
2222
from google.protobuf.struct_pb2 import Struct
@@ -220,3 +220,173 @@ def delete_event_threat_detection_custom_module(parent: str, module_id: str):
220220
print(f"Custom Module not found: {module_id}")
221221
raise e
222222
# [END securitycenter_delete_event_threat_detection_custom_module]
223+
224+
225+
# [START securitycenter_get_effective_event_threat_detection_custom_module]
226+
def get_effective_event_threat_detection_custom_module(parent: str, module_id: str):
227+
"""
228+
Retrieves an Event Threat Detection custom module using parent and module id as parameters.
229+
Args:
230+
parent: Use any one of the following options:
231+
- organizations/{organization_id}/locations/{location_id}
232+
- folders/{folder_id}/locations/{location_id}
233+
- projects/{project_id}/locations/{location_id}
234+
Returns:
235+
The retrieved Event Threat Detection custom module.
236+
Raises:
237+
NotFound: If the specified custom module does not exist.
238+
"""
239+
client = securitycentermanagement_v1.SecurityCenterManagementClient()
240+
241+
try:
242+
request = securitycentermanagement_v1.GetEffectiveEventThreatDetectionCustomModuleRequest(
243+
name=f"{parent}/effectiveEventThreatDetectionCustomModules/{module_id}",
244+
)
245+
246+
response = client.get_effective_event_threat_detection_custom_module(request=request)
247+
print(f"Retrieved Effective Event Threat Detection Custom Module: {response.name}")
248+
return response
249+
except NotFound as e:
250+
print(f"Custom Module not found: {e.message}")
251+
raise e
252+
# [END securitycenter_get_effective_event_threat_detection_custom_module]
253+
254+
255+
# [START securitycenter_list_effective_event_threat_detection_custom_module]
256+
def list_effective_event_threat_detection_custom_module(parent: str):
257+
"""
258+
Retrieves list of Event Threat Detection custom module.
259+
This includes resident modules defined at the scope of the parent,
260+
and inherited modules, inherited from ancestor organizations, folders, and projects (no descendants).
261+
262+
Args:
263+
parent: Use any one of the following options:
264+
- organizations/{organization_id}/locations/{location_id}
265+
- folders/{folder_id}/locations/{location_id}
266+
- projects/{project_id}/locations/{location_id}
267+
Returns:
268+
List of retrieved all Event Threat Detection custom modules.
269+
Raises:
270+
NotFound: If the parent resource is not found.
271+
"""
272+
273+
client = securitycentermanagement_v1.SecurityCenterManagementClient()
274+
275+
try:
276+
request = securitycentermanagement_v1.ListEffectiveEventThreatDetectionCustomModulesRequest(
277+
parent=parent,
278+
)
279+
280+
response = client.list_effective_event_threat_detection_custom_modules(request=request)
281+
282+
custom_modules = []
283+
for custom_module in response:
284+
print(f"Custom Module: {custom_module.name}")
285+
custom_modules.append(custom_module)
286+
return custom_modules
287+
except NotFound as e:
288+
print(f"Parent resource not found: {parent}")
289+
raise e
290+
except Exception as e:
291+
print(f"An error occurred while listing custom modules: {e}")
292+
raise e
293+
294+
# [END securitycenter_list_effective_event_threat_detection_custom_module]
295+
296+
297+
# [START securitycenter_list_descendant_event_threat_detection_custom_module]
298+
def list_descendant_event_threat_detection_custom_module(parent: str):
299+
"""
300+
Retrieves list of all resident Event Threat Detection custom modules and all of its descendants.
301+
302+
Args:
303+
parent: Use any one of the following options:
304+
- organizations/{organization_id}/locations/{location_id}
305+
- folders/{folder_id}/locations/{location_id}
306+
- projects/{project_id}/locations/{location_id}
307+
Returns:
308+
List of retrieved all Event Threat Detection custom modules.
309+
Raises:
310+
NotFound: If the parent resource is not found.
311+
"""
312+
313+
client = securitycentermanagement_v1.SecurityCenterManagementClient()
314+
315+
try:
316+
request = securitycentermanagement_v1.ListDescendantEventThreatDetectionCustomModulesRequest(
317+
parent=parent,
318+
)
319+
320+
response = client.list_descendant_event_threat_detection_custom_modules(request=request)
321+
322+
custom_modules = []
323+
for custom_module in response:
324+
print(f"Custom Module: {custom_module.name}")
325+
custom_modules.append(custom_module)
326+
return custom_modules
327+
except NotFound as e:
328+
print(f"Parent resource not found: {parent}")
329+
raise e
330+
except Exception as e:
331+
print(f"An error occurred while listing custom modules: {e}")
332+
raise e
333+
334+
# [END securitycenter_list_descendant_event_threat_detection_custom_module]
335+
336+
337+
# [START securitycenter_validate_event_threat_detection_custom_module]
338+
def validate_event_threat_detection_custom_module(parent: str):
339+
"""
340+
Validates a custom module for Event Threat Detection.
341+
342+
Args:
343+
parent (str): Use any one of the following options:
344+
- organizations/{organization_id}/locations/{location_id}
345+
- folders/{folder_id}/locations/{location_id}
346+
- projects/{project_id}/locations/{location_id}
347+
"""
348+
try:
349+
# Define the raw JSON configuration for the Event Threat Detection custom module
350+
raw_text = """
351+
{
352+
"ips": ["192.0.2.1"],
353+
"metadata": {
354+
"properties": {
355+
"someProperty": "someValue"
356+
},
357+
"severity": "MEDIUM"
358+
}
359+
}
360+
"""
361+
362+
# Initialize the client
363+
client = securitycentermanagement_v1.SecurityCenterManagementClient()
364+
365+
# Create the request
366+
request = securitycentermanagement_v1.ValidateEventThreatDetectionCustomModuleRequest(
367+
parent=parent,
368+
raw_text=raw_text,
369+
type="CONFIGURABLE_BAD_IP"
370+
)
371+
372+
# Perform validation
373+
response = client.validate_event_threat_detection_custom_module(request=request)
374+
375+
# Handle the response and output validation results
376+
if response.errors:
377+
print("Validation errors:")
378+
for error in response.errors:
379+
print(f"Field: {error.field_path}, Description: {error.description}")
380+
return response
381+
else:
382+
print("Validation successful: No errors found.")
383+
return response
384+
385+
except GoogleAPICallError as api_error:
386+
print(f"API call failed: {api_error}")
387+
except RetryError as retry_error:
388+
print(f"Retry error occurred: {retry_error}")
389+
except Exception as e:
390+
print(f"An unexpected error occurred: {e}")
391+
392+
# [END securitycenter_validate_event_threat_detection_custom_module]

securitycenter/snippets_management_api/event_threat_detection_custom_modules_test.py

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python
22
#
3-
# Copyright 2024 Google LLC
3+
# Copyright 2025 Google LLC
44
#
55
# Licensed under the Apache License, Version 2.0 (the "License");
66
# you may not use this file except in compliance with the License.
@@ -208,7 +208,8 @@ def test_get_event_threat_detection_custom_module():
208208

209209
assert response is not None, "Failed to retrieve the custom module."
210210
assert response.display_name.startswith(PREFIX)
211-
assert response.enablement_state == securitycentermanagement_v1.EventThreatDetectionCustomModule.EnablementState.ENABLED
211+
response_org_id = response.name.split("/")[1] # Extract organization ID from the name field
212+
assert response_org_id == ORGANIZATION_ID, f"Organization ID mismatch: Expected {ORGANIZATION_ID}, got {response_org_id}."
212213

213214

214215
@backoff.on_exception(
@@ -258,3 +259,59 @@ def test_delete_event_threat_detection_custom_module():
258259

259260
print(f"Custom module was deleted successfully: {module_id}")
260261
shared_modules.remove(module_id)
262+
263+
264+
@backoff.on_exception(
265+
backoff.expo, (InternalServerError, ServiceUnavailable, NotFound), max_tries=3
266+
)
267+
def test_get_effective_event_threat_detection_custom_module():
268+
269+
module_id = get_random_shared_module()
270+
parent = f"organizations/{ORGANIZATION_ID}/locations/{LOCATION}"
271+
272+
# Retrieve the custom module
273+
response = event_threat_detection_custom_modules.get_effective_event_threat_detection_custom_module(parent, module_id)
274+
275+
assert response is not None, "Failed to retrieve the custom module."
276+
assert response.display_name.startswith(PREFIX)
277+
response_org_id = response.name.split("/")[1] # Extract organization ID from the name field
278+
assert response_org_id == ORGANIZATION_ID, f"Organization ID mismatch: Expected {ORGANIZATION_ID}, got {response_org_id}."
279+
280+
281+
@backoff.on_exception(
282+
backoff.expo, (InternalServerError, ServiceUnavailable, NotFound), max_tries=3
283+
)
284+
def test_list_effective_event_threat_detection_custom_module():
285+
286+
parent = f"organizations/{ORGANIZATION_ID}/locations/{LOCATION}"
287+
# Retrieve the custom modules
288+
custom_modules = event_threat_detection_custom_modules.list_effective_event_threat_detection_custom_module(parent)
289+
290+
assert custom_modules is not None, "Failed to retrieve the custom modules."
291+
assert len(custom_modules) > 0, "No custom modules were retrieved."
292+
293+
294+
@backoff.on_exception(
295+
backoff.expo, (InternalServerError, ServiceUnavailable, NotFound), max_tries=3
296+
)
297+
def test_list_descendant_event_threat_detection_custom_module():
298+
299+
parent = f"organizations/{ORGANIZATION_ID}/locations/{LOCATION}"
300+
# Retrieve the custom modules
301+
custom_modules = event_threat_detection_custom_modules.list_descendant_event_threat_detection_custom_module(parent)
302+
303+
assert custom_modules is not None, "Failed to retrieve the custom modules."
304+
assert len(custom_modules) > 0, "No custom modules were retrieved."
305+
306+
307+
@backoff.on_exception(
308+
backoff.expo, (InternalServerError, ServiceUnavailable, NotFound), max_tries=3
309+
)
310+
def test_validate_event_threat_detection_custom_module():
311+
312+
parent = f"organizations/{ORGANIZATION_ID}/locations/{LOCATION}"
313+
314+
# Retrieve the custom module
315+
response = event_threat_detection_custom_modules.validate_event_threat_detection_custom_module(parent)
316+
317+
assert response is not None, "Failed to retrieve the validte ETD custom module response."

securitycenter/snippets_management_api/noxfile_config.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323

2424
TEST_CONFIG_OVERRIDE = {
2525
# You can opt out from the test for specific Python versions.
26-
"ignored_versions": ["2.7", "3.7", "3.9", "3.10", "3.11"],
26+
# SKIPPED VERSIONS: due to concurrency issues editing multiple org level
27+
# custom modules, only test these samples on latest Python.
28+
"ignored_versions": ["2.7", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12"],
2729
# An envvar key for determining the project id to use. Change it
2830
# to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a
2931
# build specific Cloud project. You can also use your own string

0 commit comments

Comments
 (0)