diff --git a/.github/workflows/utils/workflows.json b/.github/workflows/utils/workflows.json index ff954978f8..e1448a2028 100644 --- a/.github/workflows/utils/workflows.json +++ b/.github/workflows/utils/workflows.json @@ -94,5 +94,6 @@ "video-intelligence", "vision/productSearch", "workflows", - "workflows/invoke-private-endpoint" + "workflows/invoke-private-endpoint", + "security-center/snippets/v2" ] diff --git a/security-center/snippets/system-test/v2/bigqueryexport.test.js b/security-center/snippets/system-test/v2/bigqueryexport.test.js new file mode 100644 index 0000000000..717905f1cd --- /dev/null +++ b/security-center/snippets/system-test/v2/bigqueryexport.test.js @@ -0,0 +1,167 @@ +/* + * Copyright 2024 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. + */ + +const {SecurityCenterClient} = require('@google-cloud/security-center').v2; +const {assert} = require('chai'); +const {execSync} = require('child_process'); +const exec = cmd => execSync(cmd, {encoding: 'utf8'}); +const {describe, it, before} = require('mocha'); +const {BigQuery} = require('@google-cloud/bigquery'); + +const organizationId = process.env.GCLOUD_ORGANIZATION; +const projectId = process.env.GOOGLE_SAMPLES_PROJECT; +const location = 'global'; +const bigquery = new BigQuery(); + +async function cleanupDatasets() { + const [datasets] = await bigquery.getDatasets(); + for (const dataset of datasets) { + if (dataset.id.startsWith('securitycenter_')) { + console.log(`Deleting dataset: ${dataset.id}`); + await bigquery.dataset(dataset.id).delete({force: true}); + } + } +} + +async function cleanupBigQueryExports(client) { + const [exports] = await client.listBigQueryExports({ + parent: client.organizationLocationPath(organizationId, location), + }); + for (const exportData of exports) { + console.log(`Deleting BigQuery export: ${exportData.name}`); + await client.deleteBigQueryExport({name: exportData.name}); + } +} + +let dataset; + +async function createDataset() { + const randomSuffix = Math.floor(Date.now() / 1000); + const datasetId = `securitycenter_dataset_${randomSuffix}`; + const options = { + location: 'US', + }; + + try { + const [createdDataset] = await bigquery.createDataset(datasetId, options); + console.log(`Dataset ${createdDataset.id} created.`); + return createdDataset.id; + } catch (error) { + if (error.code === 409) { + // Dataset already exists - Fail the test instead of moving on + console.log( + `Dataset ${datasetId} already exists. Exiting to avoid conflict.` + ); + throw new Error(`Dataset ${datasetId} already exists.`); + } + throw error; + } +} + +describe('Client with bigquery export V2', async () => { + let data; + before(async () => { + // Creates a new client. + const client = new SecurityCenterClient(); + + // Clean up any existing datasets or BigQuery exports + await cleanupDatasets(); + await cleanupBigQueryExports(client); + + // Create a new dataset + const createdDataset = await createDataset(); + dataset = `projects/${projectId}/datasets/${createdDataset}`; + + // Build the create bigquery export request. + const bigQueryExportId = + 'bigqueryexportid-' + Math.floor(Math.random() * 10000); + const filter = 'severity="LOW" OR severity="MEDIUM"'; + const bigQueryExport = { + name: 'bigQueryExport node', + description: + 'Export low and medium findings if the compute resource has an IAM anomalous grant', + filter: filter, + dataset: dataset, + }; + const createBigQueryExportRequest = { + parent: client.organizationLocationPath(organizationId, location), + bigQueryExport, + bigQueryExportId, + }; + + try { + const response = await client.createBigQueryExport( + createBigQueryExportRequest + ); + const bigQueryExportResponse = response[0]; + data = { + orgId: organizationId, + bigQueryExportId: bigQueryExportId, + bigQueryExportName: bigQueryExportResponse.name, + untouchedbigQueryExportName: '', + }; + console.log('Created BigQuery export %j', data); + } catch (error) { + console.error('Error creating BigQuery export:', error); + } + }); + + it('client can create bigquery export V2', done => { + const output = exec( + `node v2/createBigQueryExport.js ${data.orgId} ${dataset}` + ); + assert(output.includes(data.orgId)); + assert.match(output, /BigQuery export request created successfully/); + assert.notMatch(output, /undefined/); + done(); + }); + + it('client can list all bigquery export V2', done => { + const output = exec(`node v2/listAllBigQueryExports.js ${data.orgId}`); + assert(output.includes(data.bigQueryExportName)); + assert.match(output, /Sources/); + assert.notMatch(output, /undefined/); + done(); + }); + + it('client can get a bigquery export V2', done => { + const output = exec( + `node v2/getBigQueryExport.js ${data.orgId} ${data.bigQueryExportId}` + ); + assert(output.includes(data.bigQueryExportName)); + assert.match(output, /Retrieved the BigQuery export/); + assert.notMatch(output, /undefined/); + done(); + }); + + it('client can update a bigquery export V2', done => { + const output = exec( + `node v2/updateBigQueryExport.js ${data.orgId} ${data.bigQueryExportId} ${dataset}` + ); + assert.match(output, /BigQueryExport updated successfully/); + assert.notMatch(output, /undefined/); + done(); + }); + + it('client can delete a bigquery export V2', done => { + const output = exec( + `node v2/deleteBigQueryExport.js ${data.orgId} ${data.bigQueryExportId}` + ); + assert.match(output, /BigQuery export request deleted successfully/); + assert.notMatch(output, /undefined/); + done(); + }); +}); diff --git a/security-center/snippets/v2/createBigQueryExport.js b/security-center/snippets/v2/createBigQueryExport.js index 03c23b654f..c13d05c3bf 100644 --- a/security-center/snippets/v2/createBigQueryExport.js +++ b/security-center/snippets/v2/createBigQueryExport.js @@ -26,17 +26,12 @@ function main(organizationId, dataset, location = 'global') { // Create a Security Center client const client = new SecurityCenterClient(); - /** - * Required. The name of the parent resource of the new BigQuery export. Its - * format is "organizations/[organization_id]/locations/[location_id]", - * "folders/[folder_id]/locations/[location_id]", or - * "projects/[project_id]/locations/[location_id]". - */ + // format is "organizations/[organization_id]/locations/[location_id]", + // "folders/[folder_id]/locations/[location_id]", or + // "projects/[project_id]/locations/[location_id]". const parent = client.organizationLocationPath(organizationId, location); - /** - * Required. The BigQuery export being created. - */ + // Required. The BigQuery export being created. // filter: Expression that defines the filter to apply across create/update events of findings. const filter = 'severity="LOW" OR severity="MEDIUM"'; @@ -48,12 +43,10 @@ function main(organizationId, dataset, location = 'global') { dataset, }; - /** - * Required. Unique identifier provided by the client within the parent scope. - * It must consist of only lowercase letters, numbers, and hyphens, must start - * with a letter, must end with either a letter or a number, and must be 63 - * characters or less. - */ + // Required. Unique identifier provided by the client within the parent scope. + // It must consist of only lowercase letters, numbers, and hyphens, must start + // with a letter, must end with either a letter or a number, and must be 63 + // characters or less. const bigQueryExportId = 'bigqueryexportid-' + Math.floor(Math.random() * 10000); diff --git a/security-center/snippets/v2/deleteBigQueryExport.js b/security-center/snippets/v2/deleteBigQueryExport.js index aa224e768a..2531112f33 100644 --- a/security-center/snippets/v2/deleteBigQueryExport.js +++ b/security-center/snippets/v2/deleteBigQueryExport.js @@ -27,9 +27,7 @@ function main(organizationId, exportId, location = 'global') { const client = new SecurityCenterClient(); // Build the full resource path for the BigQuery export to delete. - /* - * TODO(developer): Update the following references for your own environment before running the sample. - */ + // TODO(developer): Update the following references for your own environment before running the sample. // const organizationId = 'YOUR_ORGANIZATION_ID'; // const exportId = 'EXPORT_ID'; const name = `organizations/${organizationId}/locations/${location}/bigQueryExports/${exportId}`; diff --git a/security-center/snippets/v2/getBigQueryExport.js b/security-center/snippets/v2/getBigQueryExport.js index 12a62fee7b..286a00b801 100644 --- a/security-center/snippets/v2/getBigQueryExport.js +++ b/security-center/snippets/v2/getBigQueryExport.js @@ -27,9 +27,7 @@ function main(organizationId, exportId, location = 'global') { const client = new SecurityCenterClient(); // Build the full resource path for the BigQuery export to retrieve. - /* - * TODO(developer): Update the following references for your own environment before running the sample. - */ + // TODO(developer): Update the following references for your own environment before running the sample. // const organizationId = 'YOUR_ORGANIZATION_ID'; // const exportId = 'EXPORT_ID'; // const location = 'LOCATION_ID'; diff --git a/security-center/snippets/v2/listAllBigQueryExports.js b/security-center/snippets/v2/listAllBigQueryExports.js index d432d15ca1..0c1c0ed412 100644 --- a/security-center/snippets/v2/listAllBigQueryExports.js +++ b/security-center/snippets/v2/listAllBigQueryExports.js @@ -25,20 +25,27 @@ function main(organizationId, location = 'global') { // Creates a new client. const client = new SecurityCenterClient(); - /** - * Required. The parent, which owns the collection of BigQuery exports. Its - * format is "organizations/[organization_id]/locations/[location_id]", - * "folders/[folder_id]/locations/[location_id]", or - * "projects/[project_id]/locations/[location_id]". - */ - - /** - * TODO(developer): Update the following references for your own environment before running the sample. - */ + + // TODO(developer): Update the following references for your own environment before running the sample. // const organizationId = 'YOUR_ORGANIZATION_ID'; // const location = 'LOCATION_ID'; const parent = client.organizationLocationPath(organizationId, location); + // + // The maximum number of configs to return. The service may return fewer than + // this value. + // If unspecified, at most 10 configs will be returned. + // The maximum value is 1000; values above 1000 will be coerced to 1000. + // + // const pageSize = 1234 + // + // A page token, received from a previous `ListBigQueryExports` call. + // Provide this to retrieve the subsequent page. + // When paginating, all other parameters provided to `ListBigQueryExports` + // must match the call that provided the page token. + // + // const pageToken = 'abc123' + // Build the request. const listBigQueryExportsRequest = { parent, diff --git a/security-center/snippets/v2/updateBigQueryExport.js b/security-center/snippets/v2/updateBigQueryExport.js index 2d673fcee3..17b5f87acc 100644 --- a/security-center/snippets/v2/updateBigQueryExport.js +++ b/security-center/snippets/v2/updateBigQueryExport.js @@ -25,26 +25,15 @@ function main(organizationId, exportId, dataset, location = 'global') { // Creates a new client. const client = new SecurityCenterClient(); - /** - * Required. Name of the BigQuery export to retrieve. The following list shows - * some examples of the format: - * + - * `organizations/{organization}/locations/{location}/bigQueryExports/{export_id}` - * + `folders/{folder}/locations/{location}/bigQueryExports/{export_id}` - * + `projects/{project}locations/{location}/bigQueryExports/{export_id}` - */ - /** - * TODO(developer): Update the following references for your own environment before running the sample. - */ + // Required. Name of the BigQuery export to retrieve. + // TODO(developer): Update the following references for your own environment before running the sample. // const organizationId = 'YOUR_ORGANIZATION_ID'; // const location = 'LOCATION_ID'; // const exportId = 'EXPORT_ID'; const name = `organizations/${organizationId}/locations/${location}/bigQueryExports/${exportId}`; - /** - * Required. The BigQuery export being updated. - */ + // Required. The BigQuery export being updated. const filter = 'severity="LOW" OR severity="MEDIUM" AND category="Persistence: IAM Anomalous Grant" AND -resource.type:"compute"'; @@ -55,10 +44,8 @@ function main(organizationId, exportId, dataset, location = 'global') { filter: filter, }; - /** - * The list of fields to be updated. - * If empty all mutable fields will be updated. - */ + // The list of fields to be updated. + // If empty all mutable fields will be updated. const fieldMask = { paths: ['description', 'filter'], };