From 9500c3bfdc3aa76f7fda455a90af2f66e65eb33f Mon Sep 17 00:00:00 2001 From: Maxim Kholod Date: Mon, 11 Mar 2024 15:21:15 +0100 Subject: [PATCH] [Cloud Security] add GCP support for agentless (#177965) ## Summary Part of: - https://github.com/elastic/security-team/issues/8040 Adding support for GCP for Agentless. Specifics: - only JSON blob credentials type is supported - in contrast to Agent-based, in "GCP organisation" option there is no need to provide to `Project ID` field as it's not required for Agentless ## Screencast [screencast-github.com-2024.03.07-10_25_43.webm](https://github.com/elastic/kibana/assets/478762/cae1483c-20de-48f5-9814-b6510c1482da) ## how to test The simplest way is to deploy the Kibana image built for this PR to dev MKI env, following this documentation https://docs.elastic.dev/kibana-dev-docs/serverless/custom-kibana-image-on-serverless I tested both Org and Single Account set up with real credentials of Cloud Security Google Cloud account, got findings in the dev MKI environments ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- .../gcp_credential_form.tsx | 33 +++--- .../gcp_credentials_form_agentless.tsx | 62 ++++++++++ .../components/fleet_extensions/mocks.ts | 47 ++++++-- .../policy_template_form.test.tsx | 111 +++++++++++++++--- .../fleet_extensions/policy_template_form.tsx | 2 +- .../policy_template_selectors.tsx | 15 ++- .../use_setup_technology.test.ts | 24 +++- .../use_setup_technology.ts | 6 +- .../public/components/test_subjects.ts | 5 + 9 files changed, 257 insertions(+), 48 deletions(-) rename x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/{ => gcp_credentials_form}/gcp_credential_form.tsx (95%) create mode 100644 x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credentials_form/gcp_credentials_form_agentless.tsx diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credential_form.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credentials_form/gcp_credential_form.tsx similarity index 95% rename from x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credential_form.tsx rename to x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credentials_form/gcp_credential_form.tsx index 4039d458548bb..49c33f343e099 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credential_form.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credentials_form/gcp_credential_form.tsx @@ -25,22 +25,24 @@ import type { NewPackagePolicy } from '@kbn/fleet-plugin/public'; import { NewPackagePolicyInput, PackageInfo } from '@kbn/fleet-plugin/common'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import { GcpCredentialsType } from '../../../common/types_old'; + +import { GcpCredentialsType } from '../../../../common/types_old'; import { CLOUDBEAT_GCP, SETUP_ACCESS_CLOUD_SHELL, SETUP_ACCESS_MANUAL, -} from '../../../common/constants'; -import { CspRadioOption, RadioGroup } from './csp_boxed_radio_group'; +} from '../../../../common/constants'; +import { CspRadioOption, RadioGroup } from '../csp_boxed_radio_group'; import { getCspmCloudShellDefaultValue, getPosturePolicy, NewPackagePolicyPostureInput, -} from './utils'; -import { MIN_VERSION_GCP_CIS } from '../../common/constants'; -import { cspIntegrationDocsNavigation } from '../../common/navigation/constants'; -import { ReadDocumentation } from './aws_credentials_form/aws_credentials_form'; -import { GCP_ORGANIZATION_ACCOUNT } from './policy_template_form'; +} from '../utils'; +import { MIN_VERSION_GCP_CIS } from '../../../common/constants'; +import { cspIntegrationDocsNavigation } from '../../../common/navigation/constants'; +import { ReadDocumentation } from '../aws_credentials_form/aws_credentials_form'; +import { GCP_ORGANIZATION_ACCOUNT } from '../policy_template_form'; +import { GCP_CREDENTIALS_TYPE_OPTIONS_TEST_SUBJ } from '../../test_subjects'; export const CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS = { GOOGLE_CLOUD_SHELL_SETUP: 'google_cloud_shell_setup_test_id', @@ -51,7 +53,7 @@ export const CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS = { CREDENTIALS_JSON: 'credentials_json_test_id', }; type SetupFormatGCP = 'google_cloud_shell' | 'manual'; -const GCPSetupInfoContent = () => ( +export const GCPSetupInfoContent = () => ( <> @@ -238,7 +240,7 @@ const getSetupFormatOptions = (): CspRadioOption[] => [ defaultMessage: 'Google Cloud Shell', }), disabled: false, - testId: 'gcpGoogleCloudShellOptionTestId', + testId: GCP_CREDENTIALS_TYPE_OPTIONS_TEST_SUBJ.CLOUD_SHELL, }, { id: SETUP_ACCESS_MANUAL, @@ -246,11 +248,11 @@ const getSetupFormatOptions = (): CspRadioOption[] => [ defaultMessage: 'Manual', }), disabled: false, - testId: 'gcpManualOptionTestId', + testId: GCP_CREDENTIALS_TYPE_OPTIONS_TEST_SUBJ.MANUAL, }, ]; -interface GcpFormProps { +export interface GcpFormProps { newPolicy: NewPackagePolicy; input: Extract; updatePolicy(updatedPolicy: NewPackagePolicy): void; @@ -486,7 +488,7 @@ export const GcpCredentialsForm = ({ ); }; -const GcpInputVarFields = ({ +export const GcpInputVarFields = ({ fields, onChange, isOrganization, @@ -511,7 +513,10 @@ const GcpInputVarFields = ({ const credentialFieldValue = credentialOptionsList[0].value; const credentialJSONValue = credentialOptionsList[1].value; - const credentialsTypeValue = credentialsTypeFields?.value || credentialOptionsList[0].value; + const credentialsTypeValue = + credentialsTypeFields?.value || + (credentialFilesFields && credentialFieldValue) || + (credentialJSONFields && credentialJSONValue); return (
diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credentials_form/gcp_credentials_form_agentless.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credentials_form/gcp_credentials_form_agentless.tsx new file mode 100644 index 0000000000000..8a289ca755e29 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credentials_form/gcp_credentials_form_agentless.tsx @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiSpacer } from '@elastic/eui'; + +import { + GcpFormProps, + GCPSetupInfoContent, + GcpInputVarFields, + gcpField, + getInputVarsFields, +} from './gcp_credential_form'; +import { getPosturePolicy } from '../utils'; +import { ReadDocumentation } from '../aws_credentials_form/aws_credentials_form'; +import { cspIntegrationDocsNavigation } from '../../../common/navigation/constants'; + +export const GcpCredentialsFormAgentless = ({ + input, + newPolicy, + updatePolicy, + disabled, +}: GcpFormProps) => { + const accountType = input.streams?.[0]?.vars?.['gcp.account_type']?.value; + const isOrganization = accountType === 'organization-account'; + const organizationFields = ['gcp.organization_id', 'gcp.credentials.json']; + const singleAccountFields = ['gcp.project_id', 'gcp.credentials.json']; + + /* + For Agentless only JSON credentials type is supported. + Also in case of organisation setup, project_id is not required in contrast to Agent-based. + */ + const fields = getInputVarsFields(input, gcpField.fields).filter((field) => { + if (isOrganization) { + return organizationFields.includes(field.id); + } else { + return singleAccountFields.includes(field.id); + } + }); + + return ( + <> + + + + updatePolicy(getPosturePolicy(newPolicy, input.type, { [key]: { value } })) + } + isOrganization={isOrganization} + /> + + + + + ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts index f893545024d78..b960ac3c48e20 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts @@ -5,7 +5,7 @@ * 2.0. */ import type { NewPackagePolicy } from '@kbn/fleet-plugin/public'; -import type { PackageInfo } from '@kbn/fleet-plugin/common'; +import type { PackageInfo, PackagePolicyConfigRecord } from '@kbn/fleet-plugin/common'; import { createNewPackagePolicyMock, createAgentPolicyMock } from '@kbn/fleet-plugin/common/mocks'; import { CLOUDBEAT_GCP, @@ -17,11 +17,15 @@ import { } from '../../../common/constants'; import type { PostureInput } from '../../../common/types_old'; -export const getMockPolicyAWS = () => getPolicyMock(CLOUDBEAT_AWS, 'cspm', 'aws'); -export const getMockPolicyGCP = () => getPolicyMock(CLOUDBEAT_GCP, 'cspm', 'gcp'); -export const getMockPolicyAzure = () => getPolicyMock(CLOUDBEAT_AZURE, 'cspm', 'azure'); +export const getMockPolicyAWS = (vars?: PackagePolicyConfigRecord) => + getPolicyMock(CLOUDBEAT_AWS, 'cspm', 'aws', vars); +export const getMockPolicyGCP = (vars?: PackagePolicyConfigRecord) => + getPolicyMock(CLOUDBEAT_GCP, 'cspm', 'gcp', vars); +export const getMockPolicyAzure = (vars?: PackagePolicyConfigRecord) => + getPolicyMock(CLOUDBEAT_AZURE, 'cspm', 'azure', vars); export const getMockPolicyK8s = () => getPolicyMock(CLOUDBEAT_VANILLA, 'kspm', 'self_managed'); -export const getMockPolicyEKS = () => getPolicyMock(CLOUDBEAT_EKS, 'kspm', 'eks'); +export const getMockPolicyEKS = (vars?: PackagePolicyConfigRecord) => + getPolicyMock(CLOUDBEAT_EKS, 'kspm', 'eks', vars); export const getMockPolicyVulnMgmtAWS = () => getPolicyMock(CLOUDBEAT_VULN_MGMT_AWS, 'vuln_mgmt', 'aws'); export const getMockAgentlessAgentPolicy = () => { @@ -131,7 +135,8 @@ export const getMockPackageInfoCspmAzure = (packageVersion = '1.6.0') => { const getPolicyMock = ( type: PostureInput, posture: string, - deployment: string + deployment: string, + vars: object = {} ): NewPackagePolicy => { const mockPackagePolicy = createNewPackagePolicyMock(); @@ -204,26 +209,48 @@ const getPolicyMock = ( type: CLOUDBEAT_EKS, policy_template: 'kspm', enabled: type === CLOUDBEAT_EKS, - streams: [{ enabled: type === CLOUDBEAT_EKS, data_stream: dataStream, vars: eksVarsMock }], + streams: [ + { + enabled: type === CLOUDBEAT_EKS, + data_stream: dataStream, + vars: { ...eksVarsMock, ...vars }, + }, + ], }, { type: CLOUDBEAT_AWS, policy_template: 'cspm', enabled: type === CLOUDBEAT_AWS, - streams: [{ enabled: type === CLOUDBEAT_AWS, data_stream: dataStream, vars: awsVarsMock }], + streams: [ + { + enabled: type === CLOUDBEAT_AWS, + data_stream: dataStream, + vars: { ...awsVarsMock, ...vars }, + }, + ], }, { type: CLOUDBEAT_GCP, policy_template: 'cspm', enabled: type === CLOUDBEAT_GCP, - streams: [{ enabled: type === CLOUDBEAT_GCP, data_stream: dataStream, vars: gcpVarsMock }], + streams: [ + { + enabled: type === CLOUDBEAT_GCP, + data_stream: dataStream, + vars: { ...gcpVarsMock, ...vars }, + }, + ], }, { type: CLOUDBEAT_AZURE, policy_template: 'cspm', enabled: false, streams: [ - { enabled: type === CLOUDBEAT_AZURE, data_stream: dataStream, vars: azureVarsMock }, + { + enabled: type === CLOUDBEAT_AZURE, + data_stream: dataStream, + vars: { ...azureVarsMock, ...vars }, + }, ], }, { diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx index c7413bdac83ba..3c8b767d1bde7 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx @@ -45,11 +45,13 @@ import { useParams } from 'react-router-dom'; import { createReactQueryResponse } from '../../test/fixtures/react_query'; import { useCspSetupStatusApi } from '../../common/api/use_setup_status_api'; import { usePackagePolicyList } from '../../common/api/use_package_policy_list'; -import { CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS } from './gcp_credential_form'; +import { CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS } from './gcp_credentials_form/gcp_credential_form'; import { waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl'; import { AWS_CREDENTIALS_TYPE_OPTIONS_TEST_SUBJ, AWS_CREDENTIALS_TYPE_SELECTOR_TEST_SUBJ, + CIS_GCP_OPTION_TEST_SUBJ, + GCP_CREDENTIALS_TYPE_OPTIONS_TEST_SUBJ, SETUP_TECHNOLOGY_SELECTOR_ACCORDION_TEST_SUBJ, SETUP_TECHNOLOGY_SELECTOR_TEST_SUBJ, } from '../test_subjects'; @@ -1489,24 +1491,107 @@ describe('', () => { }); }); - it('should not render setup technology selector for KSPM', () => { + it('should render setup technology selector for GCP for organisation account type', async () => { const agentlessPolicy = getMockAgentlessAgentPolicy(); - const newPackagePolicy = getMockPolicyEKS(); + const newPackagePolicy = getMockPolicyGCP(); - const { queryByTestId } = render( - + const { getByTestId, queryByTestId, getByRole } = render( + ); + // navigate to GCP + const gcpSelectorButton = getByTestId(CIS_GCP_OPTION_TEST_SUBJ); + userEvent.click(gcpSelectorButton); + const setupTechnologySelectorAccordion = queryByTestId( SETUP_TECHNOLOGY_SELECTOR_ACCORDION_TEST_SUBJ ); + const setupTechnologySelector = getByTestId(SETUP_TECHNOLOGY_SELECTOR_TEST_SUBJ); + const orgIdField = queryByTestId(CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.ORGANIZATION_ID); + const projectIdField = queryByTestId(CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.PROJECT_ID); + const credentialsJsonField = queryByTestId( + CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.CREDENTIALS_JSON + ); + const credentialsTypSelector = queryByTestId( + CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.CREDENTIALS_TYPE + ); + const credentialsFileField = queryByTestId( + CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.CREDENTIALS_FILE + ); - expect(setupTechnologySelectorAccordion).not.toBeInTheDocument(); + // default state for GCP with the Org selected + expect(setupTechnologySelectorAccordion).toBeInTheDocument(); + expect(setupTechnologySelector).toBeInTheDocument(); + expect(setupTechnologySelector).toHaveTextContent(/agentless/i); + expect(orgIdField).toBeInTheDocument(); + expect(credentialsJsonField).toBeInTheDocument(); + expect(projectIdField).not.toBeInTheDocument(); + expect(credentialsTypSelector).not.toBeInTheDocument(); + expect(credentialsFileField).not.toBeInTheDocument(); + + // select agent-based and check for cloudformation option + userEvent.click(setupTechnologySelector); + const agentBasedOption = getByRole('option', { name: /agent-based/i }); + await waitForEuiPopoverOpen(); + userEvent.click(agentBasedOption); + await waitFor(() => { + expect(getByTestId(GCP_CREDENTIALS_TYPE_OPTIONS_TEST_SUBJ.CLOUD_SHELL)).toBeInTheDocument(); + expect(getByTestId(GCP_CREDENTIALS_TYPE_OPTIONS_TEST_SUBJ.MANUAL)).toBeInTheDocument(); + }); }); - it('should not render setup technology selector for CNVM', () => { + it('should render setup technology selector for GCP for single-account', async () => { const agentlessPolicy = getMockAgentlessAgentPolicy(); - const newPackagePolicy = getMockPolicyVulnMgmtAWS(); + const newPackagePolicy = getMockPolicyGCP({ + 'gcp.account_type': { value: GCP_SINGLE_ACCOUNT, type: 'text' }, + }); + + const { getByTestId, queryByTestId } = render( + + ); + + // navigate to GCP + const gcpSelectorButton = getByTestId(CIS_GCP_OPTION_TEST_SUBJ); + userEvent.click(gcpSelectorButton); + + const setupTechnologySelectorAccordion = queryByTestId( + SETUP_TECHNOLOGY_SELECTOR_ACCORDION_TEST_SUBJ + ); + const setupTechnologySelector = queryByTestId(SETUP_TECHNOLOGY_SELECTOR_TEST_SUBJ); + const orgIdField = queryByTestId(CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.ORGANIZATION_ID); + const projectIdField = queryByTestId(CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.PROJECT_ID); + const credentialsJsonField = queryByTestId( + CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.CREDENTIALS_JSON + ); + const credentialsTypSelector = queryByTestId( + CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.CREDENTIALS_TYPE + ); + const credentialsFileField = queryByTestId( + CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.CREDENTIALS_FILE + ); + + // default state for GCP with the Org selected + expect(setupTechnologySelectorAccordion).toBeInTheDocument(); + expect(setupTechnologySelector).toBeInTheDocument(); + expect(setupTechnologySelector).toHaveTextContent(/agentless/i); + expect(orgIdField).not.toBeInTheDocument(); + expect(credentialsJsonField).toBeInTheDocument(); + expect(projectIdField).toBeInTheDocument(); + expect(credentialsTypSelector).not.toBeInTheDocument(); + expect(credentialsFileField).not.toBeInTheDocument(); + }); + + it('should not render setup technology selector for KSPM', () => { + const agentlessPolicy = getMockAgentlessAgentPolicy(); + const newPackagePolicy = getMockPolicyEKS(); const { queryByTestId } = render( @@ -1519,16 +1604,12 @@ describe('', () => { expect(setupTechnologySelectorAccordion).not.toBeInTheDocument(); }); - it('should not render setup technology selector for CSPM GCP', () => { + it('should not render setup technology selector for CNVM', () => { const agentlessPolicy = getMockAgentlessAgentPolicy(); - const newPackagePolicy = getMockPolicyGCP(); + const newPackagePolicy = getMockPolicyVulnMgmtAWS(); const { queryByTestId } = render( - + ); const setupTechnologySelectorAccordion = queryByTestId( diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx index 303df35545db8..37c0e147c79b2 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx @@ -58,7 +58,7 @@ import { PolicyTemplateVarsForm, } from './policy_template_selectors'; import { usePackagePolicyList } from '../../common/api/use_package_policy_list'; -import { gcpField, getInputVarsFields } from './gcp_credential_form'; +import { gcpField, getInputVarsFields } from './gcp_credentials_form/gcp_credential_form'; import { SetupTechnologySelector } from './setup_technology_selector/setup_technology_selector'; import { useSetupTechnology } from './setup_technology_selector/use_setup_technology'; diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx index 7a045e7f82c0c..a7a96ba4c9174 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx @@ -23,7 +23,8 @@ import { AzureCredentialsForm } from './azure_credentials_form/azure_credentials import { AwsCredentialsForm } from './aws_credentials_form/aws_credentials_form'; import { AwsCredentialsFormAgentless } from './aws_credentials_form/aws_credentials_form_agentless'; import { EksCredentialsForm } from './eks_credentials_form'; -import { GcpCredentialsForm } from './gcp_credential_form'; +import { GcpCredentialsForm } from './gcp_credentials_form/gcp_credential_form'; +import { GcpCredentialsFormAgentless } from './gcp_credentials_form/gcp_credentials_form_agentless'; interface PolicyTemplateSelectorProps { selectedTemplate: CloudSecurityPolicyTemplate; @@ -84,16 +85,22 @@ export const PolicyTemplateVarsForm = ({ setupTechnology, ...props }: PolicyTemplateVarsFormProps) => { + const isAgentless = setupTechnology === SetupTechnology.AGENTLESS; + switch (input.type) { + case 'cloudbeat/cis_eks': + return ; case 'cloudbeat/cis_aws': - if (setupTechnology === SetupTechnology.AGENTLESS) { + if (isAgentless) { return ; } return ; - case 'cloudbeat/cis_eks': - return ; case 'cloudbeat/cis_gcp': + if (isAgentless) { + return ; + } + return ; case 'cloudbeat/cis_azure': return ; diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/setup_technology_selector/use_setup_technology.test.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/setup_technology_selector/use_setup_technology.test.ts index 744a87a009f98..0e32964fae40f 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/setup_technology_selector/use_setup_technology.test.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/setup_technology_selector/use_setup_technology.test.ts @@ -10,7 +10,7 @@ import { renderHook, act } from '@testing-library/react-hooks'; import { SetupTechnology } from '@kbn/fleet-plugin/public'; import { AgentPolicy, NewPackagePolicyInput } from '@kbn/fleet-plugin/common'; -import { CLOUDBEAT_AWS } from '../../../../common/constants'; +import { CLOUDBEAT_AWS, CLOUDBEAT_AZURE, CLOUDBEAT_GCP } from '../../../../common/constants'; import { useSetupTechnology } from './use_setup_technology'; describe('useSetupTechnology', () => { @@ -27,7 +27,7 @@ describe('useSetupTechnology', () => { expect(result.current.setupTechnology).toBe(SetupTechnology.AGENT_BASED); }); - it('sets to AGENTLESS when agentless is available', () => { + it('sets to AGENTLESS when agentless is available and AWS cloud', () => { const agentlessPolicy = { id: 'agentlessPolicyId' } as AgentPolicy; const input = { type: CLOUDBEAT_AWS } as NewPackagePolicyInput; const { result } = renderHook(() => @@ -37,6 +37,26 @@ describe('useSetupTechnology', () => { expect(result.current.setupTechnology).toBe(SetupTechnology.AGENTLESS); }); + it('sets to AGENTLESS when agentless is available and GCP cloud', () => { + const agentlessPolicy = { id: 'agentlessPolicyId' } as AgentPolicy; + const input = { type: CLOUDBEAT_GCP } as NewPackagePolicyInput; + const { result } = renderHook(() => + useSetupTechnology({ input, agentlessPolicy, isEditPage }) + ); + expect(result.current.isAgentlessAvailable).toBeTruthy(); + expect(result.current.setupTechnology).toBe(SetupTechnology.AGENTLESS); + }); + + it('sets to AGENT_BASED when agentless is available and Azure cloud', () => { + const agentlessPolicy = { id: 'agentlessPolicyId' } as AgentPolicy; + const input = { type: CLOUDBEAT_AZURE } as NewPackagePolicyInput; + const { result } = renderHook(() => + useSetupTechnology({ input, agentlessPolicy, isEditPage }) + ); + expect(result.current.isAgentlessAvailable).toBeFalsy(); + expect(result.current.setupTechnology).toBe(SetupTechnology.AGENT_BASED); + }); + it('sets to AGENT_BASED when agentPolicyId differs from agentlessPolicyId', () => { const input = { type: CLOUDBEAT_AWS } as NewPackagePolicyInput; const agentPolicy = { id: 'agentPolicyId' } as AgentPolicy; diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/setup_technology_selector/use_setup_technology.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/setup_technology_selector/use_setup_technology.ts index 4201ffde1b2a8..bcca6cd2ed41a 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/setup_technology_selector/use_setup_technology.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/setup_technology_selector/use_setup_technology.ts @@ -8,7 +8,7 @@ import { useEffect, useState } from 'react'; import { AgentPolicy, NewPackagePolicyInput } from '@kbn/fleet-plugin/common'; import { SetupTechnology } from '@kbn/fleet-plugin/public'; -import { CLOUDBEAT_AWS } from '../../../../common/constants'; +import { CLOUDBEAT_AWS, CLOUDBEAT_GCP } from '../../../../common/constants'; export const useSetupTechnology = ({ input, @@ -24,7 +24,9 @@ export const useSetupTechnology = ({ isEditPage: boolean; }) => { const isCspmAws = input.type === CLOUDBEAT_AWS; - const isAgentlessAvailable = Boolean(isCspmAws && agentlessPolicy); + const isCspmGcp = input.type === CLOUDBEAT_GCP; + const isAgentlessSupportedForCloudProvider = isCspmAws || isCspmGcp; + const isAgentlessAvailable = Boolean(isAgentlessSupportedForCloudProvider && agentlessPolicy); const agentPolicyId = agentPolicy?.id; const agentlessPolicyId = agentlessPolicy?.id; const [setupTechnology, setSetupTechnology] = useState(() => { diff --git a/x-pack/plugins/cloud_security_posture/public/components/test_subjects.ts b/x-pack/plugins/cloud_security_posture/public/components/test_subjects.ts index 250b5d01c82ce..e5c01bfb933e3 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/test_subjects.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/test_subjects.ts @@ -54,6 +54,11 @@ export const AWS_CREDENTIALS_TYPE_OPTIONS_TEST_SUBJ = { CLOUDFORMATION: 'aws-cloudformation-setup-option', MANUAL: 'aws-manual-setup-option', }; +export const GCP_CREDENTIALS_TYPE_OPTIONS_TEST_SUBJ = { + CLOUD_SHELL: 'gcpGoogleCloudShellOptionTestId', + MANUAL: 'gcpManualOptionTestId', +}; +export const CIS_GCP_OPTION_TEST_SUBJ = 'cisGcpTestId'; export const SETUP_TECHNOLOGY_SELECTOR_ACCORDION_TEST_SUBJ = 'setup-technology-selector-accordion'; export const SETUP_TECHNOLOGY_SELECTOR_TEST_SUBJ = 'setup-technology-selector';