diff --git a/security-command-center/snippets/src/main/java/vtwo/assets/AddDeleteSecurityMarks.java b/security-command-center/snippets/src/main/java/vtwo/assets/AddDeleteSecurityMarks.java new file mode 100644 index 00000000000..45e522b7c71 --- /dev/null +++ b/security-command-center/snippets/src/main/java/vtwo/assets/AddDeleteSecurityMarks.java @@ -0,0 +1,75 @@ +/* + * Copyright 2025 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. + */ + +// [START securitycenter_add_delete_security_marks_assets_v2] + +package vtwo.assets; + +import com.google.cloud.securitycenter.v2.SecurityCenterClient; +import com.google.cloud.securitycenter.v2.SecurityMarks; +import com.google.cloud.securitycenter.v2.UpdateSecurityMarksRequest; +import com.google.protobuf.FieldMask; +import java.io.IOException; + +public class AddDeleteSecurityMarks { + public static void main(String[] args) throws IOException { + // organizationId: Google Cloud Organization id. + String organizationId = "ORGANIZATION_ID"; + + // Specify the asset id. + String assetId = "ASSET_ID"; + + addAndDeleteSecurityMarks(organizationId, assetId); + } + + public static SecurityMarks addAndDeleteSecurityMarks(String organizationId, String assetId) + throws IOException { + // Initialize client that will be used to send requests. This client only needs to be created + // once, and can be reused for multiple requests. + try (SecurityCenterClient client = SecurityCenterClient.create()) { + + // Specify the value of 'assetName' in one of the following formats: + // String assetName = "organizations/{org-id}/assets/{asset-id}"; + String assetName = String.format("organizations/%s/assets/%s", organizationId, assetId); + + // Start setting up a request to clear and update security marks for an asset. + // Create security mark and field mask for clearing security marks. + SecurityMarks securityMarks = + SecurityMarks.newBuilder() + .setName(assetName + "/securityMarks") + .putMarks("key_a", "new_value_for_a") + .putMarks("key_b", "new_value_for_b") + .build(); + + // Define the paths in the updateMask that correspond to the keys being updated in + // securityMarks. + FieldMask updateMask = + FieldMask.newBuilder().addPaths("marks.key_a").addPaths("marks.key_b").build(); + + // Create the request to update security marks. + UpdateSecurityMarksRequest request = + UpdateSecurityMarksRequest.newBuilder() + .setSecurityMarks(securityMarks) + .setUpdateMask(updateMask) + .build(); + + // Call the API and return the response. + SecurityMarks response = client.updateSecurityMarks(request); + return response; + } + } +} +// [END securitycenter_add_delete_security_marks_assets_v2] diff --git a/security-command-center/snippets/src/main/java/vtwo/assets/AddSecurityMarksToAssets.java b/security-command-center/snippets/src/main/java/vtwo/assets/AddSecurityMarksToAssets.java new file mode 100644 index 00000000000..0216e65d1d1 --- /dev/null +++ b/security-command-center/snippets/src/main/java/vtwo/assets/AddSecurityMarksToAssets.java @@ -0,0 +1,79 @@ +/* + * Copyright 2025 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. + */ + +// [START securitycenter_add_security_marks_assets_v2] + +package vtwo.assets; + +import autovalue.shaded.com.google.common.collect.ImmutableMap; +import com.google.cloud.securitycenter.v2.SecurityCenterClient; +import com.google.cloud.securitycenter.v2.SecurityMarks; +import com.google.cloud.securitycenter.v2.UpdateSecurityMarksRequest; +import com.google.protobuf.FieldMask; +import java.io.IOException; + +public class AddSecurityMarksToAssets { + + public static void main(String[] args) throws IOException { + // organizationId: Google Cloud Organization id. + String organizationId = "ORGANIZATION_ID"; + + // Specify the asset id. + String assetId = "ASSET_ID"; + + addToAsset(organizationId, assetId); + } + + public static SecurityMarks addToAsset(String organizationId, String assetId) throws IOException { + // Initialize client that will be used to send requests. This client only needs to be created + // once, and can be reused for multiple requests. + try (SecurityCenterClient client = SecurityCenterClient.create()) { + + // Specify the value of 'assetName' in one of the following formats: + // String assetName = "organizations/{org-id}/assets/{asset-id}"; + String assetName = String.format("organizations/%s/assets/%s", organizationId, assetId); + + // Start setting up a request to add security marks for a finding. + ImmutableMap markMap = ImmutableMap.of("key_a", "value_a", "key_b", "value_b"); + + // Add security marks and field mask for security marks. + SecurityMarks securityMarks = + SecurityMarks.newBuilder() + .setName(assetName + "/securityMarks") + .putAllMarks(markMap) + .build(); + + // Set the update mask to specify which properties should be updated. + // If empty, all mutable fields will be updated. + // For more info on constructing field mask path, see the proto or: + // https://cloud.google.com/java/docs/reference/protobuf/latest/com.google.protobuf.FieldMask + FieldMask updateMask = + FieldMask.newBuilder().addPaths("marks.key_a").addPaths("marks.key_b").build(); + + UpdateSecurityMarksRequest request = + UpdateSecurityMarksRequest.newBuilder() + .setSecurityMarks(securityMarks) + .setUpdateMask(updateMask) + .build(); + + // Call the API and return the response. + SecurityMarks response = client.updateSecurityMarks(request); + return response; + } + } +} + +// [END securitycenter_add_security_marks_assets_v2] diff --git a/security-command-center/snippets/src/main/java/vtwo/assets/DeleteAssetsSecurityMarks.java b/security-command-center/snippets/src/main/java/vtwo/assets/DeleteAssetsSecurityMarks.java new file mode 100644 index 00000000000..07c505a5850 --- /dev/null +++ b/security-command-center/snippets/src/main/java/vtwo/assets/DeleteAssetsSecurityMarks.java @@ -0,0 +1,69 @@ +/* + * Copyright 2025 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. + */ + +// [START securitycenter_delete_security_marks_assets_v2] + +package vtwo.assets; + +import com.google.cloud.securitycenter.v2.SecurityCenterClient; +import com.google.cloud.securitycenter.v2.SecurityMarks; +import com.google.cloud.securitycenter.v2.UpdateSecurityMarksRequest; +import com.google.protobuf.FieldMask; +import java.io.IOException; + +public class DeleteAssetsSecurityMarks { + public static void main(String[] args) throws IOException { + // organizationId: Google Cloud Organization id. + String organizationId = "ORGANIZATION_ID"; + + // Specify the asset-id. + String assetId = "ASSET_ID"; + + deleteSecurityMarks(organizationId, assetId); + } + + public static SecurityMarks deleteSecurityMarks(String organizationId, String assetId) + throws IOException { + // Initialize client that will be used to send requests. This client only needs to be created + // once, and can be reused for multiple requests. + try (SecurityCenterClient client = SecurityCenterClient.create()) { + + // Specify the value of 'assetName' in one of the following formats: + // String assetName = "organizations/{org-id}/assets/{asset-id}"; + String assetName = String.format("organizations/%s/assets/%s", organizationId, assetId); + + // Start setting up a request to clear and update security marks for an asset. + // Create security mark and field mask for clearing security marks. + SecurityMarks securityMarks = + SecurityMarks.newBuilder().setName(assetName + "/securityMarks").build(); + + FieldMask updateMask = + FieldMask.newBuilder().addPaths("marks.key_a").addPaths("marks.key_b").build(); + + UpdateSecurityMarksRequest request = + UpdateSecurityMarksRequest.newBuilder() + .setSecurityMarks(securityMarks) + .setUpdateMask(updateMask) + .build(); + + // Call the API. + SecurityMarks response = client.updateSecurityMarks(request); + return response; + } + } +} + +// [END securitycenter_delete_security_marks_assets_v2] diff --git a/security-command-center/snippets/src/test/java/vtwo/AssetSecurityMarksIT.java b/security-command-center/snippets/src/test/java/vtwo/AssetSecurityMarksIT.java new file mode 100644 index 00000000000..c398f322900 --- /dev/null +++ b/security-command-center/snippets/src/test/java/vtwo/AssetSecurityMarksIT.java @@ -0,0 +1,145 @@ +/* + * Copyright 2025 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. + */ + +package vtwo; + +// import static org.junit.Assert.assertThat; +import static com.google.common.truth.Truth.assertThat; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertTrue; + +import com.google.cloud.testing.junit4.MultipleAttemptsRule; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import vtwo.assets.AddDeleteSecurityMarks; +import vtwo.assets.AddSecurityMarksToAssets; +import vtwo.assets.DeleteAssetsSecurityMarks; + +@RunWith(JUnit4.class) +public class AssetSecurityMarksIT { + + private static final String ORGANIZATION_ID = System.getenv("SCC_PROJECT_ORG_ID"); + private static String assetId; + private static ByteArrayOutputStream stdOut; + + @Rule + public final MultipleAttemptsRule multipleAttemptsRule = + new MultipleAttemptsRule(3, 120000); // 2 minutes + + // Check if the required environment variables are set. + public static void requireEnvVar(String envVarName) { + assertThat(System.getenv(envVarName)).isNotEmpty(); + } + + // Extracts the asset ID from a full resource name. + // This regex pattern matches the last segment of the resource name, + // which consists of digits after the final forward slash (e.g., "assets/12345"). + private static String extractAssetId(String assetPath) { + // Pattern to match the asset ID at the end of the resource name. + Pattern pattern = Pattern.compile("assets/([^/]+)$"); + Matcher matcher = pattern.matcher(assetPath); + if (matcher.find()) { + return matcher.group(1); + } + return assetPath; + } + + @BeforeClass + public static void setUp() throws IOException, InterruptedException { + + // Validate required environment variables. + requireEnvVar("SCC_PROJECT_ORG_ID"); + + // Load static_asset.json from resources + // Since there are no APIs to create an Asset + InputStream inputStream = + AssetSecurityMarksIT.class.getClassLoader().getResourceAsStream("static_asset.json"); + + if (inputStream == null) { + throw new IOException("static_asset.json file not found in resources."); + } + + // Convert InputStream to String + String jsonContent = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); + + // Parse JSON (using Gson) + JsonObject jsonObject = JsonParser.parseString(jsonContent).getAsJsonObject(); + + // Extract assetId from mock data + assetId = extractAssetId(jsonObject.get("name").getAsString()); + + if (assetId == null || assetId.isEmpty()) { + throw new IllegalStateException("Asset ID is missing from static_asset.json"); + } + } + + @Before + public void beforeEach() { + stdOut = new ByteArrayOutputStream(); + } + + @After + public void afterEach() { + stdOut = null; + System.setOut(null); + } + + @AfterClass + public static void cleanUp() { + System.setOut(System.out); + } + + @Test + public void testAddSecurityMarksToAsset() throws IOException { + com.google.cloud.securitycenter.v2.SecurityMarks response = + AddSecurityMarksToAssets.addToAsset(ORGANIZATION_ID, assetId); + + assertTrue(response.getMarksOrThrow("key_a").contains("value_a")); + assertTrue(response.getMarksOrThrow("key_b").contains("value_b")); + } + + @Test + public void testDeleteSecurityMarksOnAsset() throws IOException { + com.google.cloud.securitycenter.v2.SecurityMarks response = + DeleteAssetsSecurityMarks.deleteSecurityMarks(ORGANIZATION_ID, assetId); + + assertFalse(response.containsMarks("key_a")); + assertFalse(response.containsMarks("key_b")); + } + + @Test + public void testAddAndDeleteSecurityMarks() throws IOException { + com.google.cloud.securitycenter.v2.SecurityMarks response = + AddDeleteSecurityMarks.addAndDeleteSecurityMarks(ORGANIZATION_ID, assetId); + + assertTrue(response.getMarksOrThrow("key_a").contains("new_value_for_a")); + assertTrue(response.getMarksOrThrow("key_b").contains("new_value_for_b")); + } +} diff --git a/security-command-center/snippets/src/test/resources/static_asset.json b/security-command-center/snippets/src/test/resources/static_asset.json new file mode 100644 index 00000000000..81ad86c107d --- /dev/null +++ b/security-command-center/snippets/src/test/resources/static_asset.json @@ -0,0 +1,31 @@ +{ + "name": "organizations/1081635000895/assets/5259189201668787349", + "security_center_properties": { + "resource_name": "//cloudresourcemanager.googleapis.com/organizations/1081635000895", + "resource_type": "google.cloud.resourcemanager.Organization", + "resource_display_name": "cscc-client-libs-external.joonix.net" + }, + "resource_properties": { + "creationTime": "2019-03-13T21:41:55.851Z", + "displayName": "cscc-client-libs-external.joonix.net", + "lifecycleState": "ACTIVE", + "name": "organizations/1081635000895", + "organizationId": "1081635000895", + "owner": "{\"directoryCustomerId\":\"C03kcf68g\"}" + }, + "security_marks": { + "name": "organizations/1081635000895/assets/5259189201668787349/securityMarks", + "marks": { + "LEASEKEY": "1736624991011382373", + "other": "other_val" + } + }, + "create_time": { + "seconds": 1599734291, + "nanos": 993000000 + }, + "update_time": { + "seconds": 1733948390, + "nanos": 962000000 + } +}