From 94f604e7f48c434c3d0fbe9c43f7b83c5e4541e9 Mon Sep 17 00:00:00 2001 From: Ricky Ang Date: Wed, 2 Aug 2023 15:28:03 -0700 Subject: [PATCH 01/18] WIP Google Cloud Shell --- .../fleet_extensions/gcp_credential_form.tsx | 148 +++++++++++++++--- .../post_install_google_cloud_shell_modal.tsx | 116 ++++++++++++++ .../single_page_layout/hooks/form.tsx | 10 ++ .../single_page_layout/index.tsx | 9 ++ .../create_package_policy_page/types.ts | 3 +- .../enrollment_instructions/manual/index.tsx | 5 + .../components/google_cloud_shell_guide.tsx | 75 +++++++++ .../plugins/fleet/public/components/index.ts | 1 + 8 files changed, 341 insertions(+), 26 deletions(-) create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/post_install_google_cloud_shell_modal.tsx create mode 100644 x-pack/plugins/fleet/public/components/google_cloud_shell_guide.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_credential_form.tsx index ea192309310e3..2c0485de5a15b 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_credential_form.tsx @@ -4,10 +4,11 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useEffect } from 'react'; +import React, { useEffect, useRef } from 'react'; import semverLt from 'semver/functions/lt'; import semverCoerce from 'semver/functions/coerce'; import semverValid from 'semver/functions/valid'; +import { css } from '@emotion/react'; import { EuiFieldText, EuiFormRow, @@ -66,7 +67,7 @@ const DocsLink = ( defaultMessage="Read the {docs} for more details" values={{ docs: ( - + documentation ), @@ -75,13 +76,46 @@ const DocsLink = ( ); -type GcpCredentialsType = 'credentials_file' | 'credentials_json'; +const GoogleCloudShellSetup = () => { + return ( + <> + +
    +
  1. + +
  2. +
  3. + +
  4. +
  5. + +
  6. +
+
+ + + ); +}; + type GcpFields = Record; interface GcpInputFields { fields: GcpFields; } -const gcpField: GcpInputFields = { +export const gcpField: GcpInputFields = { fields: { project_id: { label: i18n.translate('xpack.csp.gcpIntegration.projectidFieldLabel', { @@ -136,7 +170,7 @@ const getSetupFormatOptions = (): Array<{ label: i18n.translate('xpack.csp.gcpIntegration.setupFormatOptions.googleCloudShell', { defaultMessage: 'Google Cloud Shell', }), - disabled: true, + disabled: false, }, { id: 'manual', @@ -175,6 +209,24 @@ const getInputVarsFields = ( } as const; }); +const getSetupFormatFromInput = ( + input: Extract< + NewPackagePolicyPostureInput, + { type: 'cloudbeat/cis_aws' | 'cloudbeat/cis_eks' | 'cloudbeat/cis_gcp' } + > +): SetupFormatGCP => { + const credentialsType = input.streams[0].vars?.setup_access?.value; + // CloudFormation is the default setup format if the integration has a CloudFormation template + if (!credentialsType) { + return 'google_cloud_shell'; + } + if (credentialsType !== 'google_cloud_shell') { + return 'manual'; + } + + return 'google_cloud_shell'; +}; + export const GcpCredentialsForm = ({ input, newPolicy, @@ -187,15 +239,63 @@ export const GcpCredentialsForm = ({ const validSemantic = semverValid(packageInfo.version); const integrationVersionNumberOnly = semverCoerce(validSemantic) || ''; const isInvalid = semverLt(integrationVersionNumberOnly, MIN_VERSION_GCP_CIS); + const fieldsSnapshot = useRef({}); + const lastSetupAccessType = useRef(undefined); + const setupFormat = getSetupFormatFromInput(input); + const getFieldById = (id: keyof GcpInputFields['fields']) => { + return fields.find((element) => element.id === id); + }; + const onSetupFormatChange = (newSetupFormat: SetupFormatGCP) => { + if (newSetupFormat === 'google_cloud_shell') { + // We need to store the current manual fields to restore them later + fieldsSnapshot.current = Object.fromEntries( + fields.map((field) => [field.id, { value: field.value }]) + ); + // We need to store the last manual credentials type to restore it later + lastSetupAccessType.current = input.streams[0].vars?.setup_access?.value; + + updatePolicy( + getPosturePolicy(newPolicy, input.type, { + setup_access: { + value: 'google_cloud_shell', + type: 'text', + }, + // Clearing fields from previous setup format to prevent exposing credentials + // when switching from manual to cloud formation + ...Object.fromEntries(fields.map((field) => [field.id, { value: undefined }])), + project_id: { + value: 'DUMMY VALUE', + type: 'text', + }, + }) + ); + } else { + updatePolicy( + getPosturePolicy(newPolicy, input.type, { + setup_access: { + // Restoring last manual credentials type or defaulting to the first option + value: lastSetupAccessType.current || 'manual', + type: 'text', + }, + // Restoring fields from manual setup format if any + ...fieldsSnapshot.current, + }) + ); + } + }; + useEffect(() => { - setIsValid(!isInvalid); + const isProjectIdEmpty = setupFormat === 'manual' && !getFieldById('project_id')?.value; + const isInvalidPolicy = isInvalid || isProjectIdEmpty; + + setIsValid(!isInvalidPolicy); onChange({ - isValid: !isInvalid, + isValid: !isInvalidPolicy, updatedPolicy: newPolicy, }); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [input, packageInfo]); + }, [input, packageInfo, setupFormat]); if (isInvalid) { return ( @@ -214,16 +314,23 @@ export const GcpCredentialsForm = ({ <> - updatePolicy(getPosturePolicy(newPolicy, input.type))} + - - updatePolicy(getPosturePolicy(newPolicy, input.type, { [key]: { value } })) - } - /> + {setupFormat === 'manual' ? ( + + updatePolicy(getPosturePolicy(newPolicy, input.type, { [key]: { value } })) + } + /> + ) : ( + + )} {DocsLink} @@ -231,15 +338,6 @@ export const GcpCredentialsForm = ({ ); }; -const GcpSetupAccessSelector = ({ onChange }: { onChange(type: GcpCredentialsType): void }) => ( - onChange(id)} - /> -); - const GcpInputVarFields = ({ fields, onChange, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/post_install_google_cloud_shell_modal.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/post_install_google_cloud_shell_modal.tsx new file mode 100644 index 0000000000000..707c24462eeb1 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/post_install_google_cloud_shell_modal.tsx @@ -0,0 +1,116 @@ +/* + * 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 { + EuiButton, + EuiButtonEmpty, + EuiCallOut, + EuiModal, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, + EuiSpacer, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useQuery } from '@tanstack/react-query'; + +import type { AgentPolicy, PackagePolicy } from '../../../../../types'; +import { + sendGetEnrollmentAPIKeys, + useCreateCloudFormationUrl, + useFleetServerHostsForPolicy, + useKibanaVersion, +} from '../../../../../hooks'; +import { getCloudFormationTemplateUrlFromPackagePolicy } from '../../../../../services'; +import { GoogleCloudShellGuide } from '../../../../../components'; +import { ManualInstructions } from '../../../../../../../components/enrollment_instructions'; + +export const PostInstallGoogleCloudShellModal: React.FunctionComponent<{ + onConfirm: () => void; + onCancel: () => void; + agentPolicy: AgentPolicy; + packagePolicy: PackagePolicy; +}> = ({ onConfirm, onCancel, agentPolicy, packagePolicy }) => { + const { data: apyKeysData } = useQuery(['googleCloudShellApiKeys'], () => + sendGetEnrollmentAPIKeys({ + page: 1, + perPage: 1, + kuery: `policy_id:${agentPolicy.id}`, + }) + ); + const { fleetServerHosts, fleetProxy } = useFleetServerHostsForPolicy(agentPolicy); + const kibanaVersion = useKibanaVersion(); + + const installManagedCommands = ManualInstructions({ + apiKey: apyKeysData?.data?.items[0]?.api_key || 'no_key', + fleetServerHosts, + fleetProxy, + kibanaVersion, + }); + + const cloudFormationTemplateUrl = + getCloudFormationTemplateUrlFromPackagePolicy(packagePolicy) || ''; + + const { cloudFormationUrl, error, isError, isLoading } = useCreateCloudFormationUrl({ + cloudFormationTemplateUrl, + enrollmentAPIKey: apyKeysData?.data?.items[0]?.api_key, + packagePolicy, + }); + + return ( + + + + + + + + + + {error && isError && ( + <> + + + + )} + + + + + + + { + window.open(cloudFormationUrl); + onConfirm(); + }} + fill + color="primary" + isLoading={isLoading} + isDisabled={isError} + > + + + + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx index 52d02d93c8097..f664e00802b5a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx @@ -304,6 +304,12 @@ export function useOnSubmit({ ? getCloudFormationTemplateUrlFromPackagePolicy(data.item) : false; + const cisGcpSetupAccess = data?.item?.inputs.find( + (element) => element?.type === 'cloudbeat/cis_gcp' + )?.streams[0].vars?.setup_access?.value; + + const hasGoogleCloudShell = cisGcpSetupAccess === 'google_cloud_shell'; + if (hasCloudFormation) { setFormState(agentCount ? 'SUBMITTED' : 'SUBMITTED_CLOUD_FORMATION'); } else { @@ -317,6 +323,10 @@ export function useOnSubmit({ setFormState('SUBMITTED_CLOUD_FORMATION'); return; } + if (!hasAgentsAssigned && hasGoogleCloudShell) { + setFormState('SUBMITTED_GOOGLE_CLOUD_SHELL'); + return; + } if (!hasAgentsAssigned) { setFormState('SUBMITTED_NO_AGENTS'); return; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx index 9b26311699ea7..e7a35ae48dbda 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx @@ -60,6 +60,7 @@ import { generateNewAgentPolicyWithDefaults } from '../../../../../../../common/ import { CreatePackagePolicySinglePageLayout, PostInstallAddAgentModal } from './components'; import { useDevToolsRequest, useOnSubmit } from './hooks'; import { PostInstallCloudFormationModal } from './components/post_install_cloud_formation_modal'; +import { PostInstallGoogleCloudShellModal } from './components/post_install_google_cloud_shell_modal'; const StepsWithLessPadding = styled(EuiSteps)` .euiStep__content { @@ -422,6 +423,14 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ onCancel={() => navigateAddAgentHelp(savedPackagePolicy)} /> )} + {formState === 'SUBMITTED_GOOGLE_CLOUD_SHELL' && agentPolicy && savedPackagePolicy && ( + navigateAddAgent(savedPackagePolicy)} + onCancel={() => navigateAddAgentHelp(savedPackagePolicy)} + /> + )} {packageInfo && ( { const enrollArgs = getfleetServerHostsEnrollArgs(apiKey, fleetServerHosts, fleetProxy); + const fleetServerUrl = enrollArgs?.split('--url=')?.pop()?.split('--enrollment')[0]; + const enrollmentToken = enrollArgs?.split('--enrollment-token=')[1]; const k8sCommand = 'kubectl apply -f elastic-agent-managed-kubernetes.yml'; @@ -62,6 +64,8 @@ sudo elastic-agent enroll ${enrollArgs} \nsudo systemctl enable elastic-agent \n sudo rpm -vi elastic-agent-${kibanaVersion}-x86_64.rpm sudo elastic-agent enroll ${enrollArgs} \nsudo systemctl enable elastic-agent \nsudo systemctl start elastic-agent`; + const googleCloudShellCommand = `FLEET_URL=${fleetServerUrl} ENROLLMENT_TOKEN=${enrollmentToken} STACK_VERSION=${kibanaVersion} ./deploy/deployment-manager/deploy.sh`; + return { linux: linuxCommand, mac: macCommand, @@ -70,5 +74,6 @@ sudo elastic-agent enroll ${enrollArgs} \nsudo systemctl enable elastic-agent \n rpm: linuxRpmCommand, kubernetes: k8sCommand, cloudFormation: '', + googleCloudShell: googleCloudShellCommand, }; }; diff --git a/x-pack/plugins/fleet/public/components/google_cloud_shell_guide.tsx b/x-pack/plugins/fleet/public/components/google_cloud_shell_guide.tsx new file mode 100644 index 0000000000000..765e0048a3107 --- /dev/null +++ b/x-pack/plugins/fleet/public/components/google_cloud_shell_guide.tsx @@ -0,0 +1,75 @@ +/* + * 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 { EuiCodeBlock, EuiLink, EuiText, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +/* Need to change to the real URL */ +const GOOGLE_CLOUD_SHELL_EXTERNAL_DOC_URL = 'https://cloud.google.com/shell/docs'; + +const Link = ({ children, url }: { children: React.ReactNode; url: string }) => ( + + {children} + +); + +export const GoogleCloudShellGuide = (props: { commandText: string }) => { + return ( + +

+ + + + ), + }} + /> +

+ +
    +
  1. + +
  2. +
  3. + <> + + + + {props.commandText} + + +
  4. +
  5. + +
  6. +
+
+
+ ); +}; diff --git a/x-pack/plugins/fleet/public/components/index.ts b/x-pack/plugins/fleet/public/components/index.ts index 8335f9fcfc61f..f578a11ab7c5a 100644 --- a/x-pack/plugins/fleet/public/components/index.ts +++ b/x-pack/plugins/fleet/public/components/index.ts @@ -30,3 +30,4 @@ export { HeaderReleaseBadge, InlineReleaseBadge } from './release_badge'; export { WithGuidedOnboardingTour } from './with_guided_onboarding_tour'; export { UninstallCommandFlyout } from './uninstall_command_flyout'; export { CloudFormationGuide } from './cloud_formation_guide'; +export { GoogleCloudShellGuide } from './google_cloud_shell_guide'; From 17fb1badaeece2f7423953296b40616419ad80a3 Mon Sep 17 00:00:00 2001 From: Ricky Ang Date: Wed, 2 Aug 2023 18:30:09 -0700 Subject: [PATCH 02/18] jest test fix + update --- .../fleet_extensions/gcp_credential_form.tsx | 8 +- .../policy_template_form.test.tsx | 89 ++++++++++++------- 2 files changed, 64 insertions(+), 33 deletions(-) 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_credential_form.tsx index 2c0485de5a15b..2ab569c4d4079 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_credential_form.tsx @@ -30,6 +30,7 @@ import { getPosturePolicy, NewPackagePolicyPostureInput } from './utils'; import { MIN_VERSION_GCP_CIS } from '../../common/constants'; export const CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS = { + GOOGLE_CLOUD_SHELL_SETUP: 'google_cloud_shell_setup_test_id', PROJECT_ID: 'project_id_test_id', CREDENTIALS_TYPE: 'credentials_type_test_id', CREDENTIALS_FILE: 'credentials_file_test_id', @@ -79,7 +80,11 @@ const DocsLink = ( const GoogleCloudShellSetup = () => { return ( <> - +
    { return fields.find((element) => element.id === id); }; + const onSetupFormatChange = (newSetupFormat: SetupFormatGCP) => { if (newSetupFormat === 'google_cloud_shell') { // We need to store the current manual fields to restore them later 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 808a3164fb41c..dd5e78b14a1db 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 @@ -983,12 +983,12 @@ describe('', () => { let policy = getMockPolicyGCP(); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { credentials_type: { value: 'credentials-file' }, + setup_access: { value: 'manual' }, }); const { getByText } = render( ); - expect(onChange).toHaveBeenCalledWith({ isValid: false, updatedPolicy: policy, @@ -1001,45 +1001,80 @@ describe('', () => { ).toBeInTheDocument(); }); - it(`renders ${CLOUDBEAT_GCP} Credentials File fields`, () => { + it(`renders Google Cloud Shell forms when Setup Access is set to Google Cloud Shell`, () => { let policy = getMockPolicyGCP(); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { credentials_type: { value: 'credentials-file' }, + setup_access: { value: 'google_cloud_shell' }, }); - const { getByLabelText, getByRole } = render( + const { getByTestId } = render( ); - - expect(getByRole('option', { name: 'Credentials File', selected: true })).toBeInTheDocument(); + expect(onChange).toHaveBeenCalledWith({ + isValid: true, + updatedPolicy: policy, + }); expect( - getByLabelText('Path to JSON file containing the credentials and key used to subscribe') + getByTestId(CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.GOOGLE_CLOUD_SHELL_SETUP) ).toBeInTheDocument(); }); - it(`updates ${CLOUDBEAT_GCP} Credentials File fields`, () => { + it(`project ID is required for Manual users`, () => { + let policy = getMockPolicyGCP(); + policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { + project_id: { value: undefined }, + setup_access: { value: 'manual' }, + }); + + const { rerender } = render( + + ); + expect(onChange).toHaveBeenCalledWith({ + isValid: false, + updatedPolicy: policy, + }); + policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { + project_id: { value: '' }, + setup_access: { value: 'manual' }, + }); + rerender(); + expect(onChange).toHaveBeenCalledWith({ + isValid: false, + updatedPolicy: policy, + }); + }); + + it(`renders ${CLOUDBEAT_GCP} Credentials File fields`, () => { let policy = getMockPolicyGCP(); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { credentials_type: { value: 'credentials-file' }, + setup_access: { value: 'manual' }, }); - const { rerender, getByTestId } = render( + const { getByLabelText, getByRole } = render( ); - userEvent.type(getByTestId(CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.PROJECT_ID), 'a'); + expect(getByRole('option', { name: 'Credentials File', selected: true })).toBeInTheDocument(); + expect( + getByLabelText('Path to JSON file containing the credentials and key used to subscribe') + ).toBeInTheDocument(); + }); + + it(`updates ${CLOUDBEAT_GCP} Credentials File fields`, () => { + let policy = getMockPolicyGCP(); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { project_id: { value: 'a' }, + credentials_type: { value: 'credentials-file' }, + setup_access: { value: 'manual' }, }); - expect(onChange).toHaveBeenCalledWith({ - isValid: true, - updatedPolicy: policy, - }); - - rerender(); + const { getByTestId } = render( + + ); userEvent.type(getByTestId(CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.CREDENTIALS_FILE), 'b'); @@ -1047,7 +1082,7 @@ describe('', () => { credentials_file: { value: 'b' }, }); - expect(onChange).toHaveBeenNthCalledWith(5, { + expect(onChange).toHaveBeenCalledWith({ isValid: true, updatedPolicy: policy, }); @@ -1056,10 +1091,11 @@ describe('', () => { it(`renders ${CLOUDBEAT_GCP} Credentials JSON fields`, () => { let policy = getMockPolicyGCP(); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { + setup_access: { value: 'manual' }, credentials_type: { value: 'credentials-json' }, }); - const { getByLabelText, getByRole } = render( + const { getByRole, getByLabelText } = render( ); @@ -1073,33 +1109,22 @@ describe('', () => { it(`updates ${CLOUDBEAT_GCP} Credentials JSON fields`, () => { let policy = getMockPolicyGCP(); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { + project_id: { value: 'a' }, credentials_type: { value: 'credentials-json' }, + setup_access: { value: 'manual' }, }); - const { rerender, getByTestId } = render( + const { getByTestId } = render( ); - userEvent.type(getByTestId(CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.PROJECT_ID), 'a'); - - policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { - project_id: { value: 'a' }, - }); - - expect(onChange).toHaveBeenCalledWith({ - isValid: true, - updatedPolicy: policy, - }); - - rerender(); - userEvent.type(getByTestId(CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.CREDENTIALS_JSON), 'b'); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { credentials_json: { value: 'b' }, }); - expect(onChange).toHaveBeenNthCalledWith(5, { + expect(onChange).toHaveBeenCalledWith({ isValid: true, updatedPolicy: policy, }); From a0127fe7760c532e1b66e8ea0aab67eec37e9c46 Mon Sep 17 00:00:00 2001 From: Ricky Ang Date: Thu, 3 Aug 2023 08:33:55 -0700 Subject: [PATCH 03/18] more updates --- .../single_page_layout/hooks/form.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx index f664e00802b5a..a26cbbf5aba18 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx @@ -315,6 +315,11 @@ export function useOnSubmit({ } else { setFormState(agentCount ? 'SUBMITTED' : 'SUBMITTED_NO_AGENTS'); } + if (hasGoogleCloudShell) { + setFormState(agentCount ? 'SUBMITTED' : 'SUBMITTED_GOOGLE_CLOUD_SHELL'); + } else { + setFormState(agentCount ? 'SUBMITTED' : 'SUBMITTED_NO_AGENTS'); + } if (!error) { setSavedPackagePolicy(data!.item); From f8097c2707e16b85944da567c3bb95bf06430a03 Mon Sep 17 00:00:00 2001 From: Ricky Ang Date: Thu, 3 Aug 2023 16:26:06 -0700 Subject: [PATCH 04/18] post install modal now has active url --- .../fleet_extensions/gcp_credential_form.tsx | 76 +++++++++++++++++-- .../components/fleet_extensions/utils.ts | 19 +++++ .../post_install_google_cloud_shell_modal.tsx | 11 +-- .../single_page_layout/hooks/form.tsx | 12 +-- x-pack/plugins/fleet/public/hooks/index.ts | 1 + .../hooks/use_create_cloud_shell_url.ts | 52 +++++++++++++ ...loud_shell_url_from_package_policy.test.ts | 61 +++++++++++++++ ...et_cloud_shell_url_from_package_policy.tsx | 27 +++++++ x-pack/plugins/fleet/public/services/index.ts | 1 + 9 files changed, 241 insertions(+), 19 deletions(-) create mode 100644 x-pack/plugins/fleet/public/hooks/use_create_cloud_shell_url.ts create mode 100644 x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_package_policy.test.ts create mode 100644 x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_package_policy.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_credential_form.tsx index 2ab569c4d4079..d37758c17f2ab 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_credential_form.tsx @@ -25,8 +25,13 @@ 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 { CLOUDBEAT_GCP } from '../../../common/constants'; import { RadioGroup } from './csp_boxed_radio_group'; -import { getPosturePolicy, NewPackagePolicyPostureInput } from './utils'; +import { + getCspmCloudShellDefaultValue, + getPosturePolicy, + NewPackagePolicyPostureInput, +} from './utils'; import { MIN_VERSION_GCP_CIS } from '../../common/constants'; export const CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS = { @@ -232,6 +237,65 @@ const getSetupFormatFromInput = ( return 'google_cloud_shell'; }; +const getGoogleCloudShellUrl = (newPolicy: NewPackagePolicy) => { + const template: string | undefined = newPolicy?.inputs?.find((i) => i.type === CLOUDBEAT_GCP) + ?.config?.cloud_shell_url?.value; + + return template || undefined; +}; + +const updateCloudShellUrl = ( + newPolicy: NewPackagePolicy, + updatePolicy: (policy: NewPackagePolicy) => void, + templateUrl: string | undefined +) => { + updatePolicy?.({ + ...newPolicy, + inputs: newPolicy.inputs.map((input) => { + if (input.type === CLOUDBEAT_GCP) { + return { + ...input, + config: { cloud_shell_url: { value: templateUrl } }, + }; + } + return input; + }), + }); +}; + +const useCloudShellUrl = ({ + packageInfo, + newPolicy, + updatePolicy, + setupFormat, +}: { + packageInfo: PackageInfo; + newPolicy: NewPackagePolicy; + updatePolicy: (policy: NewPackagePolicy) => void; + setupFormat: SetupFormatGCP; +}) => { + useEffect(() => { + const policyInputCloudShellUrl = getGoogleCloudShellUrl(newPolicy); + + if (setupFormat === 'manual') { + if (!!policyInputCloudShellUrl) { + updateCloudShellUrl(newPolicy, updatePolicy, undefined); + } + return; + } + const templateUrl = getCspmCloudShellDefaultValue(packageInfo); + + // If the template is not available, do not update the policy + if (templateUrl === '') return; + + // If the template is already set, do not update the policy + if (policyInputCloudShellUrl === templateUrl) return; + + updateCloudShellUrl(newPolicy, updatePolicy, templateUrl); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [newPolicy?.vars?.cloud_shell_url, newPolicy, packageInfo, setupFormat]); +}; + export const GcpCredentialsForm = ({ input, newPolicy, @@ -251,6 +315,12 @@ export const GcpCredentialsForm = ({ return fields.find((element) => element.id === id); }; + useCloudShellUrl({ + packageInfo, + newPolicy, + updatePolicy, + setupFormat, + }); const onSetupFormatChange = (newSetupFormat: SetupFormatGCP) => { if (newSetupFormat === 'google_cloud_shell') { // We need to store the current manual fields to restore them later @@ -269,10 +339,6 @@ export const GcpCredentialsForm = ({ // Clearing fields from previous setup format to prevent exposing credentials // when switching from manual to cloud formation ...Object.fromEntries(fields.map((field) => [field.id, { value: undefined }])), - project_id: { - value: 'DUMMY VALUE', - type: 'text', - }, }) ); } else { diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts index 6e8aecf5daf1b..6376f2b828670 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts @@ -225,3 +225,22 @@ export const getMaxPackageName = ( return `${packageName}-${maxPkgPolicyName + 1}`; }; + +export const getCspmCloudShellDefaultValue = (packageInfo: PackageInfo): string => { + if (!packageInfo.policy_templates) return ''; + + const policyTemplate = packageInfo.policy_templates.find((p) => p.name === CSPM_POLICY_TEMPLATE); + if (!policyTemplate) return ''; + + const policyTemplateInputs = hasPolicyTemplateInputs(policyTemplate) && policyTemplate.inputs; + + if (!policyTemplateInputs) return ''; + + const cloudShellUrl = policyTemplateInputs.reduce((acc, input): string => { + if (!input.vars) return acc; + const template = input.vars.find((v) => v.name === 'cloud_shell_url')?.default; + return template ? String(template) : acc; + }, ''); + + return cloudShellUrl; +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/post_install_google_cloud_shell_modal.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/post_install_google_cloud_shell_modal.tsx index 707c24462eeb1..3879f44d5fbe0 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/post_install_google_cloud_shell_modal.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/post_install_google_cloud_shell_modal.tsx @@ -23,11 +23,10 @@ import { useQuery } from '@tanstack/react-query'; import type { AgentPolicy, PackagePolicy } from '../../../../../types'; import { sendGetEnrollmentAPIKeys, - useCreateCloudFormationUrl, + useCreateCloudShellUrl, useFleetServerHostsForPolicy, useKibanaVersion, } from '../../../../../hooks'; -import { getCloudFormationTemplateUrlFromPackagePolicy } from '../../../../../services'; import { GoogleCloudShellGuide } from '../../../../../components'; import { ManualInstructions } from '../../../../../../../components/enrollment_instructions'; @@ -54,11 +53,7 @@ export const PostInstallGoogleCloudShellModal: React.FunctionComponent<{ kibanaVersion, }); - const cloudFormationTemplateUrl = - getCloudFormationTemplateUrlFromPackagePolicy(packagePolicy) || ''; - - const { cloudFormationUrl, error, isError, isLoading } = useCreateCloudFormationUrl({ - cloudFormationTemplateUrl, + const { cloudShellUrl, error, isError, isLoading } = useCreateCloudShellUrl({ enrollmentAPIKey: apyKeysData?.data?.items[0]?.api_key, packagePolicy, }); @@ -97,7 +92,7 @@ export const PostInstallGoogleCloudShellModal: React.FunctionComponent<{ { - window.open(cloudFormationUrl); + window.open(cloudShellUrl); onConfirm(); }} fill diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx index a26cbbf5aba18..3857af4cee72c 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx @@ -24,7 +24,11 @@ import { sendBulkInstallPackages, sendGetPackagePolicies, } from '../../../../../hooks'; -import { isVerificationError, packageToPackagePolicy } from '../../../../../services'; +import { + getCloudShellUrlFromPackagePolicy, + isVerificationError, + packageToPackagePolicy, +} from '../../../../../services'; import { FLEET_ELASTIC_AGENT_PACKAGE, FLEET_SYSTEM_PACKAGE, @@ -304,11 +308,7 @@ export function useOnSubmit({ ? getCloudFormationTemplateUrlFromPackagePolicy(data.item) : false; - const cisGcpSetupAccess = data?.item?.inputs.find( - (element) => element?.type === 'cloudbeat/cis_gcp' - )?.streams[0].vars?.setup_access?.value; - - const hasGoogleCloudShell = cisGcpSetupAccess === 'google_cloud_shell'; + const hasGoogleCloudShell = data?.item ? getCloudShellUrlFromPackagePolicy(data.item) : false; if (hasCloudFormation) { setFormState(agentCount ? 'SUBMITTED' : 'SUBMITTED_CLOUD_FORMATION'); diff --git a/x-pack/plugins/fleet/public/hooks/index.ts b/x-pack/plugins/fleet/public/hooks/index.ts index 0692ce961379e..eaddfbaa08009 100644 --- a/x-pack/plugins/fleet/public/hooks/index.ts +++ b/x-pack/plugins/fleet/public/hooks/index.ts @@ -33,3 +33,4 @@ export * from './use_fleet_server_hosts_for_policy'; export * from './use_fleet_server_standalone'; export * from './use_locator'; export * from './use_create_cloud_formation_url'; +export * from './use_create_cloud_shell_url'; diff --git a/x-pack/plugins/fleet/public/hooks/use_create_cloud_shell_url.ts b/x-pack/plugins/fleet/public/hooks/use_create_cloud_shell_url.ts new file mode 100644 index 0000000000000..895c3a3d7b31b --- /dev/null +++ b/x-pack/plugins/fleet/public/hooks/use_create_cloud_shell_url.ts @@ -0,0 +1,52 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +import type { PackagePolicy } from '../../common'; +import { getCloudShellUrlFromPackagePolicy } from '../services'; + +import { useGetSettings } from './use_request'; + +export const useCreateCloudShellUrl = ({ + enrollmentAPIKey, + packagePolicy, +}: { + enrollmentAPIKey: string | undefined; + packagePolicy?: PackagePolicy; +}) => { + const { data, isLoading } = useGetSettings(); + + let isError = false; + let error: string | undefined; + + // Default fleet server host + const fleetServerHost = data?.item.fleet_server_hosts?.[0]; + + if (!fleetServerHost && !isLoading) { + isError = true; + error = i18n.translate('xpack.fleet.agentEnrollment.cloudFormation.noFleetServerHost', { + defaultMessage: 'No Fleet Server host found', + }); + } + + if (!enrollmentAPIKey && !isLoading) { + isError = true; + error = i18n.translate('xpack.fleet.agentEnrollment.cloudFormation.noApiKey', { + defaultMessage: 'No enrollment token found', + }); + } + + const cloudShellUrl = getCloudShellUrlFromPackagePolicy(packagePolicy) || ''; + + return { + isLoading, + cloudShellUrl, + isError, + error, + }; +}; diff --git a/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_package_policy.test.ts b/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_package_policy.test.ts new file mode 100644 index 0000000000000..9d255c6f8ce75 --- /dev/null +++ b/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_package_policy.test.ts @@ -0,0 +1,61 @@ +/* + * 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 { getCloudShellUrlFromPackagePolicy } from './get_cloud_shell_url_from_package_policy'; + +describe('getCloudShellUrlFromPackagePolicyy', () => { + test('returns undefined when packagePolicy is undefined', () => { + const result = getCloudShellUrlFromPackagePolicy(undefined); + expect(result).toBeUndefined(); + }); + + test('returns undefined when packagePolicy is defined but inputs are empty', () => { + const packagePolicy = { inputs: [] }; + // @ts-expect-error + const result = getCloudShellUrlFromPackagePolicy(packagePolicy); + expect(result).toBeUndefined(); + }); + + test('returns undefined when no enabled input has a cloudFormationTemplateUrl', () => { + const packagePolicy = { + inputs: [ + { enabled: false, config: { cloud_shell_url: { value: 'url1' } } }, + { enabled: false, config: { cloud_shell_url: { value: 'url2' } } }, + ], + }; + // @ts-expect-error + const result = getCloudShellUrlFromPackagePolicy(packagePolicy); + expect(result).toBeUndefined(); + }); + + test('returns the cloudFormationTemplateUrl of the first enabled input', () => { + const packagePolicy = { + inputs: [ + { enabled: false, config: { cloud_shell_url: { value: 'url1' } } }, + { enabled: true, config: { cloud_shell_url: { value: 'url2' } } }, + { enabled: true, config: { cloud_shell_url: { value: 'url3' } } }, + ], + }; + // @ts-expect-error + const result = getCloudShellUrlFromPackagePolicy(packagePolicy); + expect(result).toBe('url2'); + }); + + test('returns the cloudFormationTemplateUrl of the first enabled input and ignores subsequent inputs', () => { + const packagePolicy = { + inputs: [ + { enabled: true, config: { cloud_shell_url: { value: 'url1' } } }, + { enabled: true, config: { cloud_shell_url: { value: 'url2' } } }, + { enabled: true, config: { cloud_shell_url: { value: 'url3' } } }, + ], + }; + // @ts-expect-error + const result = getCloudShellUrlFromPackagePolicy(packagePolicy); + expect(result).toBe('url1'); + }); + + // Add more test cases as needed +}); diff --git a/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_package_policy.tsx b/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_package_policy.tsx new file mode 100644 index 0000000000000..b6eef94d1f8bf --- /dev/null +++ b/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_package_policy.tsx @@ -0,0 +1,27 @@ +/* + * 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 type { PackagePolicy } from '../types'; + +/** + * Get the cloud formation template url from a package policy + * It looks for a config with a cloud_formation_template_url object present in + * the enabled inputs of the package policy + */ +export const getCloudShellUrlFromPackagePolicy = (packagePolicy?: PackagePolicy) => { + const cloudShellUrl = packagePolicy?.inputs?.reduce((accInput, input) => { + if (accInput !== '') { + return accInput; + } + if (input?.enabled && input?.config?.cloud_shell_url) { + return input.config.cloud_shell_url.value; + } + return accInput; + }, ''); + + return cloudShellUrl !== '' ? cloudShellUrl : undefined; +}; diff --git a/x-pack/plugins/fleet/public/services/index.ts b/x-pack/plugins/fleet/public/services/index.ts index 1da10c7384cdc..5ff4b4092c7c3 100644 --- a/x-pack/plugins/fleet/public/services/index.ts +++ b/x-pack/plugins/fleet/public/services/index.ts @@ -50,3 +50,4 @@ export { createExtensionRegistrationCallback } from './ui_extensions'; export { incrementPolicyName } from './increment_policy_name'; export { getCloudFormationTemplateUrlFromPackagePolicy } from './get_cloud_formation_template_url_from_package_policy'; export { getCloudFormationTemplateUrlFromAgentPolicy } from './get_cloud_formation_template_url_from_agent_policy'; +export { getCloudShellUrlFromPackagePolicy } from './get_cloud_shell_url_from_package_policy'; From 6b1c3fe200c998bfbdbd71684e30dd54a7aaa414 Mon Sep 17 00:00:00 2001 From: Ricky Ang Date: Thu, 3 Aug 2023 22:21:38 -0700 Subject: [PATCH 05/18] UI update to include Organization option (disabled) --- .../fleet_extensions/gcp_credential_form.tsx | 2 +- .../fleet_extensions/policy_template_form.tsx | 73 +++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) 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_credential_form.tsx index d37758c17f2ab..8e822b77b69c3 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_credential_form.tsx @@ -355,7 +355,7 @@ export const GcpCredentialsForm = ({ ); } }; - + // Integration is Invalid IF Version is not at least 1.5.0 OR Setup Access is manual but Project ID is empty useEffect(() => { const isProjectIdEmpty = setupFormat === 'manual' && !getFieldById('project_id')?.value; const isInvalidPolicy = isInvalid || isProjectIdEmpty; 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 5242de03a0058..3b24dd161723a 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 @@ -81,6 +81,8 @@ interface IntegrationInfoFieldsProps { export const AWS_SINGLE_ACCOUNT = 'single-account'; export const AWS_ORGANIZATION_ACCOUNT = 'organization-account'; +export const GCP_SINGLE_ACCOUNT = 'single-account-gcp'; +export const GCP_ORGANIZATION_ACCOUNT = 'organization-account-gcp'; type AwsAccountType = typeof AWS_SINGLE_ACCOUNT | typeof AWS_ORGANIZATION_ACCOUNT; const getAwsAccountTypeOptions = (isAwsOrgDisabled: boolean): CspRadioGroupProps['options'] => [ @@ -104,6 +106,28 @@ const getAwsAccountTypeOptions = (isAwsOrgDisabled: boolean): CspRadioGroupProps }, ]; +const getGcpAccountTypeOptions = (): CspRadioGroupProps['options'] => [ + { + id: GCP_ORGANIZATION_ACCOUNT, + label: i18n.translate('xpack.csp.fleetIntegration.gcpAccountType.gcpOrganizationLabel', { + defaultMessage: 'GCP Organization', + }), + disabled: true, + tooltip: i18n.translate( + 'xpack.csp.fleetIntegration.gcpAccountType.gcpOrganizationDisabledTooltip', + { + defaultMessage: 'Coming Soon', + } + ), + }, + { + id: GCP_SINGLE_ACCOUNT, + label: i18n.translate('xpack.csp.fleetIntegration.gcpAccountType.gcpSingleAccountLabel', { + defaultMessage: 'Single Account', + }), + }, +]; + const getAwsAccountType = ( input: Extract ): AwsAccountType | undefined => input.streams[0].vars?.['aws.account_type']?.value; @@ -208,6 +232,46 @@ const AwsAccountTypeSelect = ({ ); }; +const GcpAccountTypeSelect = ({ + input, + newPolicy, + updatePolicy, + packageInfo, +}: { + input: Extract; + newPolicy: NewPackagePolicy; + updatePolicy: (updatedPolicy: NewPackagePolicy) => void; + packageInfo: PackageInfo; +}) => { + return ( + <> + + + + + { + updatePolicy( + getPosturePolicy(newPolicy, input.type, { + gcp_account_type: { + value: accountType, + type: 'text', + }, + }) + ); + }} + size="m" + /> + + + ); +}; + const IntegrationSettings = ({ onChange, fields }: IntegrationInfoFieldsProps) => (
    {fields.map(({ value, id, label, error }) => ( @@ -375,6 +439,15 @@ export const CspPolicyTemplateForm = memo )} + {input.type === 'cloudbeat/cis_gcp' && ( + + )} + {/* Defines the name/description */} Date: Fri, 4 Aug 2023 22:30:21 -0700 Subject: [PATCH 06/18] Added agent installation flyout for Google Cloud Shell --- .../fleet_extensions/gcp_credential_form.tsx | 4 +- .../fleet_extensions/policy_template_form.tsx | 7 +++ .../utils/install_command_utils.ts | 4 ++ .../google_cloud_shell_instructions.tsx | 43 +++++++++++++ .../agent_enrollment_flyout/hooks.tsx | 7 ++- .../agent_enrollment_flyout/index.tsx | 2 +- .../agent_enrollment_flyout/types.ts | 1 + .../install_section.tsx | 1 + .../public/components/platform_selector.tsx | 63 ++++++++++++++----- .../fleet/public/hooks/use_platform.tsx | 20 +++++- .../get_cloud_shell_url_from_agent_policy.ts | 32 ++++++++++ x-pack/plugins/fleet/public/services/index.ts | 1 + 12 files changed, 166 insertions(+), 19 deletions(-) create mode 100644 x-pack/plugins/fleet/public/components/agent_enrollment_flyout/google_cloud_shell_instructions.tsx create mode 100644 x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_agent_policy.ts 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_credential_form.tsx index 8e822b77b69c3..2a1a42e3b689f 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_credential_form.tsx @@ -20,6 +20,7 @@ import { EuiForm, EuiCallOut, EuiTextArea, + EuiHorizontalRule, } from '@elastic/eui'; import type { NewPackagePolicy } from '@kbn/fleet-plugin/public'; import { NewPackagePolicyInput, PackageInfo } from '@kbn/fleet-plugin/common'; @@ -44,7 +45,8 @@ export const CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS = { type SetupFormatGCP = 'google_cloud_shell' | 'manual'; const GCPSetupInfoContent = () => ( <> - + +

    + + + + ); }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.ts index 0221d332692b9..b528c5092ea14 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.ts @@ -52,6 +52,9 @@ function getArtifact(platform: PLATFORM_TYPE, kibanaVersion: string) { kubernetes: { downloadCommand: '', }, + googleCloudShell: { + downloadCommand: '', + }, }; return artifactMap[platform]; @@ -116,6 +119,7 @@ export function getInstallCommandForPlatform( rpm: `${artifact.downloadCommand}\nsudo elastic-agent enroll ${commandArgumentsStr}\nsudo systemctl enable elastic-agent\nsudo systemctl start elastic-agent`, kubernetes: '', cloudFormation: '', + googleCloudShell: '', }; return commands[platform]; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/google_cloud_shell_instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/google_cloud_shell_instructions.tsx new file mode 100644 index 0000000000000..89b6f67fe0dcf --- /dev/null +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/google_cloud_shell_instructions.tsx @@ -0,0 +1,43 @@ +/* + * 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 { EuiButton, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { GoogleCloudShellGuide } from '../google_cloud_shell_guide'; + +interface Props { + cloudShellUrl: string; + cloudShellCommand: string; +} + +export const GoogleCloudShellInstructions: React.FunctionComponent = ({ + cloudShellUrl, + cloudShellCommand, +}) => { + return ( + <> + + + + + + + ); +}; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/hooks.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/hooks.tsx index 12f49550b5fcb..8bc272d20c14d 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/hooks.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/hooks.tsx @@ -14,7 +14,10 @@ import { FLEET_CLOUD_SECURITY_POSTURE_PACKAGE, FLEET_CLOUD_DEFEND_PACKAGE, } from '../../../common'; -import { getCloudFormationTemplateUrlFromAgentPolicy } from '../../services'; +import { + getCloudFormationTemplateUrlFromAgentPolicy, + getCloudShellUrlFromAgentPolicy, +} from '../../services'; import type { K8sMode, CloudSecurityIntegrationType } from './types'; @@ -81,10 +84,12 @@ export function useCloudSecurityIntegration(agentPolicy?: AgentPolicy) { const integrationType = getCloudSecurityIntegrationTypeFromPackagePolicy(agentPolicy); const cloudformationUrl = getCloudFormationTemplateUrlFromAgentPolicy(agentPolicy); + const cloudShellUrl = getCloudShellUrlFromAgentPolicy(agentPolicy); return { integrationType, cloudformationUrl, + cloudShellUrl, }; }, [agentPolicy]); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx index 869b2326b6f47..2317ae13c3033 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx @@ -155,7 +155,7 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ )} - {selectionType === 'tabs' ? ( + {selectionType === 'tabs' && !cloudSecurityIntegration?.cloudShellUrl ? ( <> diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts index 79c8029e4ec01..8ba8b5e973041 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts @@ -23,6 +23,7 @@ export type SelectionType = 'tabs' | 'radio' | undefined; export interface CloudSecurityIntegration { integrationType: CloudSecurityIntegrationType | undefined; cloudformationUrl: string | undefined; + cloudShellUrl: string | undefined; } export interface BaseProps { diff --git a/x-pack/plugins/fleet/public/components/enrollment_instructions/install_section.tsx b/x-pack/plugins/fleet/public/components/enrollment_instructions/install_section.tsx index b4f90212d8625..5c236e4aa7c09 100644 --- a/x-pack/plugins/fleet/public/components/enrollment_instructions/install_section.tsx +++ b/x-pack/plugins/fleet/public/components/enrollment_instructions/install_section.tsx @@ -44,6 +44,7 @@ export const InstallSection: React.FunctionComponent = ({ windowsCommand={installCommand.windows} linuxDebCommand={installCommand.deb} linuxRpmCommand={installCommand.rpm} + googleCloudShellCommand={installCommand.googleCloudShell} k8sCommand={installCommand.kubernetes} hasK8sIntegration={isK8s === 'IS_KUBERNETES' || isK8s === 'IS_KUBERNETES_MULTIPAGE'} cloudSecurityIntegration={cloudSecurityIntegration} diff --git a/x-pack/plugins/fleet/public/components/platform_selector.tsx b/x-pack/plugins/fleet/public/components/platform_selector.tsx index eb8b9d898855e..a4f08c265a565 100644 --- a/x-pack/plugins/fleet/public/components/platform_selector.tsx +++ b/x-pack/plugins/fleet/public/components/platform_selector.tsx @@ -24,9 +24,15 @@ import { FLEET_CLOUD_SECURITY_POSTURE_CSPM_POLICY_TEMPLATE, } from '../../common/constants/epm'; import { type PLATFORM_TYPE } from '../hooks'; -import { REDUCED_PLATFORM_OPTIONS, PLATFORM_OPTIONS, usePlatform } from '../hooks'; +import { + REDUCED_PLATFORM_OPTIONS, + PLATFORM_OPTIONS, + PLATFORM_OPTIONS_CLOUD_SHELL, + usePlatform, +} from '../hooks'; import { KubernetesInstructions } from './agent_enrollment_flyout/kubernetes_instructions'; +import { GoogleCloudShellInstructions } from './agent_enrollment_flyout/google_cloud_shell_instructions'; import type { CloudSecurityIntegration } from './agent_enrollment_flyout/types'; interface Props { @@ -36,6 +42,7 @@ interface Props { linuxDebCommand: string; linuxRpmCommand: string; k8sCommand: string; + googleCloudShellCommand?: string | undefined; hasK8sIntegration: boolean; cloudSecurityIntegration?: CloudSecurityIntegration | undefined; hasK8sIntegrationMultiPage: boolean; @@ -58,6 +65,7 @@ export const PlatformSelector: React.FunctionComponent = ({ linuxDebCommand, linuxRpmCommand, k8sCommand, + googleCloudShellCommand, hasK8sIntegration, cloudSecurityIntegration, hasK8sIntegrationMultiPage, @@ -68,6 +76,9 @@ export const PlatformSelector: React.FunctionComponent = ({ onCopy, }) => { const getInitialPlatform = useCallback(() => { + if (cloudSecurityIntegration?.cloudShellUrl) { + return 'googleCloudShell'; + } if ( hasK8sIntegration || (cloudSecurityIntegration?.integrationType === @@ -77,19 +88,28 @@ export const PlatformSelector: React.FunctionComponent = ({ return 'kubernetes'; return 'linux'; - }, [hasK8sIntegration, cloudSecurityIntegration?.integrationType, isManaged]); + }, [ + hasK8sIntegration, + cloudSecurityIntegration?.integrationType, + isManaged, + cloudSecurityIntegration?.cloudShellUrl, + ]); const { platform, setPlatform } = usePlatform(getInitialPlatform()); // In case of fleet server installation or standalone agent without // Kubernetes integration in the policy use reduced platform options + // If it has Cloud Shell URL, then it should show platform options with Cloudshell in it const isReduced = hasFleetServer || (!isManaged && !hasK8sIntegration); const getPlatformOptions = useCallback(() => { const platformOptions = isReduced ? REDUCED_PLATFORM_OPTIONS : PLATFORM_OPTIONS; + const platformOptionsWithCloudShell = cloudSecurityIntegration?.cloudShellUrl + ? PLATFORM_OPTIONS_CLOUD_SHELL + : platformOptions; - return platformOptions; - }, [isReduced]); + return platformOptionsWithCloudShell; + }, [isReduced, cloudSecurityIntegration?.cloudShellUrl]); const [copyButtonClicked, setCopyButtonClicked] = useState(false); @@ -144,6 +164,7 @@ export const PlatformSelector: React.FunctionComponent = ({ deb: linuxDebCommand, rpm: linuxRpmCommand, kubernetes: k8sCommand, + googleCloudShell: k8sCommand, }; const onTextAreaClick = () => { if (onCopy) onCopy(); @@ -208,6 +229,15 @@ export const PlatformSelector: React.FunctionComponent = ({ )} + {platform === 'googleCloudShell' && isManaged && ( + <> + + + + )} {!hasK8sIntegrationMultiPage && ( <> {platform === 'kubernetes' && ( @@ -220,17 +250,20 @@ export const PlatformSelector: React.FunctionComponent = ({ )} - - {commandsByPlatform[platform]} - + {platform !== 'googleCloudShell' && ( + + {commandsByPlatform[platform]} + + )} + {fullCopyButton && ( diff --git a/x-pack/plugins/fleet/public/hooks/use_platform.tsx b/x-pack/plugins/fleet/public/hooks/use_platform.tsx index cc35477fbef12..7a5a4aae323b3 100644 --- a/x-pack/plugins/fleet/public/hooks/use_platform.tsx +++ b/x-pack/plugins/fleet/public/hooks/use_platform.tsx @@ -8,7 +8,14 @@ import { useState } from 'react'; import { i18n } from '@kbn/i18n'; -export type PLATFORM_TYPE = 'linux' | 'mac' | 'windows' | 'rpm' | 'deb' | 'kubernetes'; +export type PLATFORM_TYPE = + | 'linux' + | 'mac' + | 'windows' + | 'rpm' + | 'deb' + | 'kubernetes' + | 'googleCloudShell'; export const REDUCED_PLATFORM_OPTIONS: Array<{ label: string; @@ -63,6 +70,17 @@ export const PLATFORM_OPTIONS = [ }, ]; +export const PLATFORM_OPTIONS_CLOUD_SHELL = [ + ...PLATFORM_OPTIONS, + { + id: 'googleCloudShell', + label: i18n.translate('xpack.fleet.enrollmentInstructions.platformButtons.googleCloudShell', { + defaultMessage: 'Google Cloud Shell Script', + }), + 'data-test-subj': 'platformTypeGoogleCloudShellScript', + }, +]; + export function usePlatform(initialPlatform: PLATFORM_TYPE = 'linux') { const [platform, setPlatform] = useState(initialPlatform); diff --git a/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_agent_policy.ts b/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_agent_policy.ts new file mode 100644 index 0000000000000..2ae3c6b3a7c99 --- /dev/null +++ b/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_agent_policy.ts @@ -0,0 +1,32 @@ +/* + * 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 type { AgentPolicy } from '../types'; + +/** + * Get the cloud formation template url from a agent policy + * It looks for a config with a cloud_formation_template_url object present in + * the enabled package_policies inputs of the agent policy + */ +export const getCloudShellUrlFromAgentPolicy = (selectedPolicy?: AgentPolicy) => { + const cloudShellUrl = selectedPolicy?.package_policies?.reduce((acc, packagePolicy) => { + const findCloudShellUrlConfig = packagePolicy.inputs?.reduce((accInput, input) => { + if (accInput !== '') { + return accInput; + } + if (input?.enabled && input?.config?.cloud_shell_url) { + return input.config.cloud_shell_url.value; + } + return accInput; + }, ''); + if (findCloudShellUrlConfig) { + return findCloudShellUrlConfig; + } + return acc; + }, ''); + return cloudShellUrl !== '' ? cloudShellUrl : undefined; +}; diff --git a/x-pack/plugins/fleet/public/services/index.ts b/x-pack/plugins/fleet/public/services/index.ts index 5ff4b4092c7c3..38fb4bd528bb0 100644 --- a/x-pack/plugins/fleet/public/services/index.ts +++ b/x-pack/plugins/fleet/public/services/index.ts @@ -51,3 +51,4 @@ export { incrementPolicyName } from './increment_policy_name'; export { getCloudFormationTemplateUrlFromPackagePolicy } from './get_cloud_formation_template_url_from_package_policy'; export { getCloudFormationTemplateUrlFromAgentPolicy } from './get_cloud_formation_template_url_from_agent_policy'; export { getCloudShellUrlFromPackagePolicy } from './get_cloud_shell_url_from_package_policy'; +export { getCloudShellUrlFromAgentPolicy } from './get_cloud_shell_url_from_agent_policy'; From 1fe23a119cdf91388a69253e52a4c2474ed8a8a8 Mon Sep 17 00:00:00 2001 From: Ricky Ang Date: Fri, 4 Aug 2023 23:39:49 -0700 Subject: [PATCH 07/18] fix check failure --- .../components/enrollment_instructions/standalone/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/fleet/public/components/enrollment_instructions/standalone/index.tsx b/x-pack/plugins/fleet/public/components/enrollment_instructions/standalone/index.tsx index 6994cf2a7ebc2..ca2754daf1b71 100644 --- a/x-pack/plugins/fleet/public/components/enrollment_instructions/standalone/index.tsx +++ b/x-pack/plugins/fleet/public/components/enrollment_instructions/standalone/index.tsx @@ -38,5 +38,6 @@ cd elastic-agent-${kibanaVersion}-windows-x86_64 deb: linuxDebCommand, rpm: linuxRpmCommand, kubernetes: k8sCommand, + googleCloudShell: '', }; }; From a483c22f1e15732394655ac49956dc6bf58df184 Mon Sep 17 00:00:00 2001 From: Ricky Ang Date: Sat, 5 Aug 2023 11:53:05 -0700 Subject: [PATCH 08/18] bug fix + test for getCloudShellFromAgentPolicy --- .../fleet_extensions/gcp_credential_form.tsx | 2 +- .../agent_enrollment_flyout/index.tsx | 2 +- .../agent_enrollment_flyout/instructions.tsx | 9 +- .../components/google_cloud_shell_guide.tsx | 85 ++++++++++--------- ..._cloud_shell_url_from_agent_policy.test.ts | 68 +++++++++++++++ ...loud_shell_url_from_package_policy.test.ts | 6 +- 6 files changed, 123 insertions(+), 49 deletions(-) create mode 100644 x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_agent_policy.test.ts 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_credential_form.tsx index 2a1a42e3b689f..fc8f634884d75 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_credential_form.tsx @@ -450,7 +450,7 @@ const GcpInputVarFields = ({ data-test-subj={CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.CREDENTIALS_TYPE} fullWidth options={credentialOptionsList} - value={credentialsTypeFields?.value} + value={credentialsTypeFields?.value || credentialOptionsList[0].value} onChange={(optionElem) => { onChange('credentials_type', optionElem.target.value); }} diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx index 2317ae13c3033..869b2326b6f47 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx @@ -155,7 +155,7 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ )} - {selectionType === 'tabs' && !cloudSecurityIntegration?.cloudShellUrl ? ( + {selectionType === 'tabs' ? ( <> diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx index b602b9fd8931d..b76b646833e5f 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx @@ -80,8 +80,11 @@ export const Instructions = (props: InstructionProps) => { (fleetStatus.missingRequirements ?? []).some((r) => r === FLEET_SERVER_PACKAGE)); useEffect(() => { - // If we have a cloudFormationTemplateUrl, we want to hide the selection type - if (props.cloudSecurityIntegration?.cloudformationUrl) { + // If we have a cloudFormationTemplateUrl or cloudShellUrl, we want to hide the selection type + if ( + props.cloudSecurityIntegration?.cloudformationUrl || + props.cloudSecurityIntegration?.cloudShellUrl + ) { setSelectionType(undefined); } else if (!isIntegrationFlow && showAgentEnrollment) { setSelectionType('radio'); @@ -103,7 +106,7 @@ export const Instructions = (props: InstructionProps) => { } else if (showAgentEnrollment) { return ( <> - {selectionType === 'tabs' && ( + {selectionType === 'tabs' && !props.cloudSecurityIntegration?.cloudShellUrl && ( <> export const GoogleCloudShellGuide = (props: { commandText: string }) => { return ( - -

    - + <> + + +

    + + + + ), + }} + /> +

    + +
      +
    1. + +
    2. +
    3. + <> - - ), - }} - /> -

      - -
        -
      1. - -
      2. -
      3. - <> + + + {props.commandText} + + +
      4. +
      5. - - - {props.commandText} - - -
      6. -
      7. - -
      8. -
      +
    4. +
    +
    -
    + ); }; diff --git a/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_agent_policy.test.ts b/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_agent_policy.test.ts new file mode 100644 index 0000000000000..ad7711917bd4a --- /dev/null +++ b/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_agent_policy.test.ts @@ -0,0 +1,68 @@ +/* + * 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 { getCloudShellUrlFromAgentPolicy } from './get_cloud_shell_url_from_agent_policy'; + +describe('getCloudShellUrlFromAgentPolicy', () => { + it('should return undefined when selectedPolicy is undefined', () => { + const result = getCloudShellUrlFromAgentPolicy(); + expect(result).toBeUndefined(); + }); + + it('should return undefined when selectedPolicy has no package_policies', () => { + const selectedPolicy = {}; + // @ts-expect-error + const result = getCloudShellUrlFromAgentPolicy(selectedPolicy); + expect(result).toBeUndefined(); + }); + + it('should return undefined when no input has enabled and config.cloud_shell_url', () => { + const selectedPolicy = { + package_policies: [ + { + inputs: [ + { enabled: false, config: {} }, + { enabled: true, config: {} }, + { enabled: true, config: { other_property: 'value' } }, + ], + }, + { + inputs: [ + { enabled: false, config: {} }, + { enabled: false, config: {} }, + ], + }, + ], + }; + // @ts-expect-error + const result = getCloudShellUrlFromAgentPolicy(selectedPolicy); + expect(result).toBeUndefined(); + }); + + it('should return the first config.cloud_shell_url when available', () => { + const selectedPolicy = { + package_policies: [ + { + inputs: [ + { enabled: false, config: { cloud_shell_url: { value: 'url1' } } }, + { enabled: false, config: { cloud_shell_url: { value: 'url2' } } }, + { enabled: false, config: { other_property: 'value' } }, + ], + }, + { + inputs: [ + { enabled: false, config: {} }, + { enabled: true, config: { cloud_shell_url: { value: 'url3' } } }, + { enabled: true, config: { cloud_shell_url: { value: 'url4' } } }, + ], + }, + ], + }; + // @ts-expect-error + const result = getCloudShellUrlFromAgentPolicy(selectedPolicy); + expect(result).toBe('url3'); + }); +}); diff --git a/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_package_policy.test.ts b/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_package_policy.test.ts index 9d255c6f8ce75..389a481e98f8a 100644 --- a/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_package_policy.test.ts +++ b/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_package_policy.test.ts @@ -19,7 +19,7 @@ describe('getCloudShellUrlFromPackagePolicyy', () => { expect(result).toBeUndefined(); }); - test('returns undefined when no enabled input has a cloudFormationTemplateUrl', () => { + test('returns undefined when no enabled input has a CloudShellUrl', () => { const packagePolicy = { inputs: [ { enabled: false, config: { cloud_shell_url: { value: 'url1' } } }, @@ -31,7 +31,7 @@ describe('getCloudShellUrlFromPackagePolicyy', () => { expect(result).toBeUndefined(); }); - test('returns the cloudFormationTemplateUrl of the first enabled input', () => { + test('returns the CloudShellUrl of the first enabled input', () => { const packagePolicy = { inputs: [ { enabled: false, config: { cloud_shell_url: { value: 'url1' } } }, @@ -44,7 +44,7 @@ describe('getCloudShellUrlFromPackagePolicyy', () => { expect(result).toBe('url2'); }); - test('returns the cloudFormationTemplateUrl of the first enabled input and ignores subsequent inputs', () => { + test('returns the CloudShellUrl of the first enabled input and ignores subsequent inputs', () => { const packagePolicy = { inputs: [ { enabled: true, config: { cloud_shell_url: { value: 'url1' } } }, From 359ea43e69118acd0e1a258c47bf2d69c7ed5ac4 Mon Sep 17 00:00:00 2001 From: Ricky Ang Date: Sat, 5 Aug 2023 12:01:58 -0700 Subject: [PATCH 09/18] translate id update --- .../plugins/fleet/public/hooks/use_create_cloud_shell_url.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/fleet/public/hooks/use_create_cloud_shell_url.ts b/x-pack/plugins/fleet/public/hooks/use_create_cloud_shell_url.ts index 895c3a3d7b31b..d8d26d45846b0 100644 --- a/x-pack/plugins/fleet/public/hooks/use_create_cloud_shell_url.ts +++ b/x-pack/plugins/fleet/public/hooks/use_create_cloud_shell_url.ts @@ -29,14 +29,14 @@ export const useCreateCloudShellUrl = ({ if (!fleetServerHost && !isLoading) { isError = true; - error = i18n.translate('xpack.fleet.agentEnrollment.cloudFormation.noFleetServerHost', { + error = i18n.translate('xpack.fleet.agentEnrollment.cloudShell.noFleetServerHost', { defaultMessage: 'No Fleet Server host found', }); } if (!enrollmentAPIKey && !isLoading) { isError = true; - error = i18n.translate('xpack.fleet.agentEnrollment.cloudFormation.noApiKey', { + error = i18n.translate('xpack.fleet.agentEnrollment.cloudShell.noApiKey', { defaultMessage: 'No enrollment token found', }); } From fdeea1b7ddfabe041dfc4ccd68605ea54fa6f5f1 Mon Sep 17 00:00:00 2001 From: Ricky Ang Date: Wed, 9 Aug 2023 18:13:27 -0700 Subject: [PATCH 10/18] pr comments --- .../aws_credentials_form.tsx | 2 +- .../fleet_extensions/gcp_credential_form.tsx | 24 ++++--------------- .../enrollment_instructions/manual/index.tsx | 2 +- .../get_cloud_shell_url_from_agent_policy.ts | 4 ++-- ...et_cloud_shell_url_from_package_policy.tsx | 4 ++-- 5 files changed, 10 insertions(+), 26 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/aws_credentials_form.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/aws_credentials_form.tsx index 6dd52f259066e..4b50ccbd73c8a 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/aws_credentials_form.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/aws_credentials_form.tsx @@ -167,7 +167,7 @@ const Link = ({ children, url }: { children: React.ReactNode; url: string }) => ); -const ReadDocumentation = ({ url }: { url: string }) => { +export const ReadDocumentation = ({ url }: { url: string }) => { return ( ( ); -/* NEED TO FIND THE REAL URL HERE LATER */ -const DocsLink = ( - - - documentation - - ), - }} - /> - -); - const GoogleCloudShellSetup = () => { return ( <> @@ -228,7 +212,7 @@ const getSetupFormatFromInput = ( > ): SetupFormatGCP => { const credentialsType = input.streams[0].vars?.setup_access?.value; - // CloudFormation is the default setup format if the integration has a CloudFormation template + // Google Cloud shell is the default value if (!credentialsType) { return 'google_cloud_shell'; } @@ -406,7 +390,7 @@ export const GcpCredentialsForm = ({ )} - {DocsLink} + ); diff --git a/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx b/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx index e1bb7a01bf5ec..ce3015dd2ccbd 100644 --- a/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx +++ b/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx @@ -64,7 +64,7 @@ sudo elastic-agent enroll ${enrollArgs} \nsudo systemctl enable elastic-agent \n sudo rpm -vi elastic-agent-${kibanaVersion}-x86_64.rpm sudo elastic-agent enroll ${enrollArgs} \nsudo systemctl enable elastic-agent \nsudo systemctl start elastic-agent`; - const googleCloudShellCommand = `FLEET_URL=${fleetServerUrl} ENROLLMENT_TOKEN=${enrollmentToken} STACK_VERSION=${kibanaVersion} ./deploy/deployment-manager/deploy.sh`; + const googleCloudShellCommand = `FLEET_URL=${fleetServerUrl} ENROLLMENT_TOKEN=${enrollmentToken} STACK_VERSION=${kibanaVersion} ./deploy.sh`; return { linux: linuxCommand, diff --git a/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_agent_policy.ts b/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_agent_policy.ts index 2ae3c6b3a7c99..b41bfa9981859 100644 --- a/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_agent_policy.ts +++ b/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_agent_policy.ts @@ -8,8 +8,8 @@ import type { AgentPolicy } from '../types'; /** - * Get the cloud formation template url from a agent policy - * It looks for a config with a cloud_formation_template_url object present in + * Get the cloud shell url from a agent policy + * It looks for a config with a cloud_shell_url object present in * the enabled package_policies inputs of the agent policy */ export const getCloudShellUrlFromAgentPolicy = (selectedPolicy?: AgentPolicy) => { diff --git a/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_package_policy.tsx b/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_package_policy.tsx index b6eef94d1f8bf..158ead0b39ce3 100644 --- a/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_package_policy.tsx +++ b/x-pack/plugins/fleet/public/services/get_cloud_shell_url_from_package_policy.tsx @@ -8,8 +8,8 @@ import type { PackagePolicy } from '../types'; /** - * Get the cloud formation template url from a package policy - * It looks for a config with a cloud_formation_template_url object present in + * Get the cloud shell url from a package policy + * It looks for a config with a cloud_shell_url object present in * the enabled inputs of the package policy */ export const getCloudShellUrlFromPackagePolicy = (packagePolicy?: PackagePolicy) => { From 0322e9a6903adb5681128c24af388692433c2bb8 Mon Sep 17 00:00:00 2001 From: Ricky Ang Date: Thu, 10 Aug 2023 08:20:28 -0700 Subject: [PATCH 11/18] pr comments + improvements on widget --- .../common/constants.ts | 2 + .../public/common/constants.ts | 2 +- .../components/accounts_evaluated_widget.tsx | 73 ++++++++++++------- .../fleet_extensions/gcp_credential_form.tsx | 31 ++++---- 4 files changed, 68 insertions(+), 40 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/common/constants.ts b/x-pack/plugins/cloud_security_posture/common/constants.ts index 91d42c2544191..2d8b8157ee758 100644 --- a/x-pack/plugins/cloud_security_posture/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/common/constants.ts @@ -125,3 +125,5 @@ export const VULNERABILITIES_SEVERITY: Record = { }; export const VULNERABILITIES_ENUMERATION = 'CVE'; +export const SETUP_ACCESS_CLOUD_SHELL = 'google_cloud_shell'; +export const SETUP_ACCESS_MANUAL = 'manual'; diff --git a/x-pack/plugins/cloud_security_posture/public/common/constants.ts b/x-pack/plugins/cloud_security_posture/public/common/constants.ts index 3ef666a1c110f..a08ebb48fdd01 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/constants.ts @@ -75,7 +75,7 @@ export const cloudPostureIntegrations: CloudPostureIntegrations = { { type: CLOUDBEAT_AWS, name: i18n.translate('xpack.csp.cspmIntegration.awsOption.nameTitle', { - defaultMessage: 'Amazon Web Services', + defaultMessage: 'AWS', }), benchmark: i18n.translate('xpack.csp.cspmIntegration.awsOption.benchmarkTitle', { defaultMessage: 'CIS AWS', diff --git a/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx b/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx index 2709163c22a84..c912a61224757 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx @@ -10,6 +10,7 @@ import { CIS_AWS, CIS_GCP } from '../../common/constants'; import { Cluster } from '../../common/types'; import { CISBenchmarkIcon } from './cis_benchmark_icon'; import { CompactFormattedNumber } from './compact_formatted_number'; +import { useNavigateFindings } from '../common/hooks/use_navigate_findings'; export const AccountsEvaluatedWidget = ({ clusters, @@ -23,6 +24,12 @@ export const AccountsEvaluatedWidget = ({ return clusters?.filter((obj) => obj?.meta.benchmark.id === benchmarkId) || []; }; + const navToFindings = useNavigateFindings(); + + const navToFindingsByCloudProvider = (provider: string) => { + navToFindings({ 'cloud.provider': provider }); + }; + const cisAwsClusterAmount = filterClustersById(CIS_AWS).length; const cisGcpClusterAmount = filterClustersById(CIS_GCP).length; @@ -32,32 +39,46 @@ export const AccountsEvaluatedWidget = ({ return ( <> - - - - - - - - - - - - - - - - - - - - + {cisAwsClusterAmount > 0 && ( + + + + + + { + navToFindingsByCloudProvider('aws'); + }} + > + + + + + )} + {cisGcpClusterAmount > 0 && ( + + + + + + { + navToFindingsByCloudProvider('gcp'); + }} + > + + + + + )} ); 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_credential_form.tsx index 0ade33440db14..9d699ea31107f 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_credential_form.tsx @@ -25,7 +25,11 @@ 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 { CLOUDBEAT_GCP } from '../../../common/constants'; +import { + CLOUDBEAT_GCP, + SETUP_ACCESS_CLOUD_SHELL, + SETUP_ACCESS_MANUAL, +} from '../../../common/constants'; import { RadioGroup } from './csp_boxed_radio_group'; import { getCspmCloudShellDefaultValue, @@ -162,14 +166,14 @@ const getSetupFormatOptions = (): Array<{ disabled: boolean; }> => [ { - id: 'google_cloud_shell', + id: SETUP_ACCESS_CLOUD_SHELL, label: i18n.translate('xpack.csp.gcpIntegration.setupFormatOptions.googleCloudShell', { defaultMessage: 'Google Cloud Shell', }), disabled: false, }, { - id: 'manual', + id: SETUP_ACCESS_MANUAL, label: i18n.translate('xpack.csp.gcpIntegration.setupFormatOptions.manual', { defaultMessage: 'Manual', }), @@ -214,13 +218,13 @@ const getSetupFormatFromInput = ( const credentialsType = input.streams[0].vars?.setup_access?.value; // Google Cloud shell is the default value if (!credentialsType) { - return 'google_cloud_shell'; + return SETUP_ACCESS_CLOUD_SHELL; } - if (credentialsType !== 'google_cloud_shell') { - return 'manual'; + if (credentialsType !== SETUP_ACCESS_CLOUD_SHELL) { + return SETUP_ACCESS_MANUAL; } - return 'google_cloud_shell'; + return SETUP_ACCESS_CLOUD_SHELL; }; const getGoogleCloudShellUrl = (newPolicy: NewPackagePolicy) => { @@ -263,7 +267,7 @@ const useCloudShellUrl = ({ useEffect(() => { const policyInputCloudShellUrl = getGoogleCloudShellUrl(newPolicy); - if (setupFormat === 'manual') { + if (setupFormat === SETUP_ACCESS_MANUAL) { if (!!policyInputCloudShellUrl) { updateCloudShellUrl(newPolicy, updatePolicy, undefined); } @@ -308,7 +312,7 @@ export const GcpCredentialsForm = ({ setupFormat, }); const onSetupFormatChange = (newSetupFormat: SetupFormatGCP) => { - if (newSetupFormat === 'google_cloud_shell') { + if (newSetupFormat === SETUP_ACCESS_CLOUD_SHELL) { // We need to store the current manual fields to restore them later fieldsSnapshot.current = Object.fromEntries( fields.map((field) => [field.id, { value: field.value }]) @@ -319,7 +323,7 @@ export const GcpCredentialsForm = ({ updatePolicy( getPosturePolicy(newPolicy, input.type, { setup_access: { - value: 'google_cloud_shell', + value: SETUP_ACCESS_CLOUD_SHELL, type: 'text', }, // Clearing fields from previous setup format to prevent exposing credentials @@ -332,7 +336,7 @@ export const GcpCredentialsForm = ({ getPosturePolicy(newPolicy, input.type, { setup_access: { // Restoring last manual credentials type or defaulting to the first option - value: lastSetupAccessType.current || 'manual', + value: lastSetupAccessType.current || SETUP_ACCESS_MANUAL, type: 'text', }, // Restoring fields from manual setup format if any @@ -343,7 +347,8 @@ export const GcpCredentialsForm = ({ }; // Integration is Invalid IF Version is not at least 1.5.0 OR Setup Access is manual but Project ID is empty useEffect(() => { - const isProjectIdEmpty = setupFormat === 'manual' && !getFieldById('project_id')?.value; + const isProjectIdEmpty = + setupFormat === SETUP_ACCESS_MANUAL && !getFieldById('project_id')?.value; const isInvalidPolicy = isInvalid || isProjectIdEmpty; setIsValid(!isInvalidPolicy); @@ -379,7 +384,7 @@ export const GcpCredentialsForm = ({ onChange={onSetupFormatChange} /> - {setupFormat === 'manual' ? ( + {setupFormat === SETUP_ACCESS_MANUAL ? ( From f4cc8836059f87a7e26dffa4105c9bbdc8d393a7 Mon Sep 17 00:00:00 2001 From: Ricky Ang Date: Thu, 10 Aug 2023 09:13:28 -0700 Subject: [PATCH 12/18] fix failed jest --- .../components/fleet_extensions/policy_template_form.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 dd5e78b14a1db..213d03b95266f 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 @@ -196,7 +196,7 @@ describe('', () => { it('renders CSPM input selector', () => { const { getByLabelText } = render(); - const option1 = getByLabelText('Amazon Web Services'); + const option1 = getByLabelText('AWS'); const option2 = getByLabelText('GCP'); const option3 = getByLabelText('Azure'); @@ -229,7 +229,7 @@ describe('', () => { ); - const option1 = getByLabelText('Amazon Web Services'); + const option1 = getByLabelText('AWS'); const option2 = getByLabelText('GCP'); const option3 = getByLabelText('Azure'); From 10572c2161619424da30d5963c218b4583fe2169 Mon Sep 17 00:00:00 2001 From: Ricky Ang Date: Thu, 10 Aug 2023 13:58:00 -0700 Subject: [PATCH 13/18] added beta tag and some unit test for getCspmCloudShellDefaultValue to make it easier when refactoring later --- .../public/common/constants.ts | 2 + .../csp_boxed_radio_group.tsx | 14 +- .../components/fleet_extensions/utils.test.ts | 131 +++++++++++++++++- .../components/fleet_extensions/utils.ts | 1 + 4 files changed, 146 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/common/constants.ts b/x-pack/plugins/cloud_security_posture/public/common/constants.ts index a08ebb48fdd01..6ef38730dcf34 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/constants.ts @@ -59,6 +59,7 @@ export interface CloudPostureIntegrationProps { disabled?: boolean; icon?: string; tooltip?: string; + isBeta?: boolean; }>; } @@ -91,6 +92,7 @@ export const cloudPostureIntegrations: CloudPostureIntegrations = { defaultMessage: 'CIS GCP', }), icon: 'logoGCP', + isBeta: true, }, { type: CLOUDBEAT_AZURE, diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/csp_boxed_radio_group.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/csp_boxed_radio_group.tsx index 97018a072abd0..c1305aeafd7ff 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/csp_boxed_radio_group.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/csp_boxed_radio_group.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { useEuiTheme, EuiButton, EuiRadio, EuiToolTip } from '@elastic/eui'; +import { useEuiTheme, EuiButton, EuiRadio, EuiToolTip, EuiBetaBadge } from '@elastic/eui'; import { css } from '@emotion/react'; export interface CspRadioGroupProps { @@ -23,6 +23,7 @@ interface CspRadioOption { label: string; icon?: string; tooltip?: string; + isBeta?: boolean; } export const RadioGroup = ({ @@ -58,6 +59,8 @@ export const RadioGroup = ({ anchorProps={{ style: { flexGrow: 1, + /* Set Maximum width ONLY for Integration type buttons, we know this because only Integration Type has icons */ + ...(option.icon && { maxWidth: `calc(${euiTheme.size.xxs} * 115)` }), }, }} > @@ -105,6 +108,15 @@ export const RadioGroup = ({ checked={isChecked} onChange={() => {}} /> + {option.isBeta && ( +
    + +
    + )} ); diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.test.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.test.ts index 323ab6aa7ef05..b710741652b6c 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.test.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.test.ts @@ -5,8 +5,14 @@ * 2.0. */ -import { getMaxPackageName, getPostureInputHiddenVars, getPosturePolicy } from './utils'; +import { + getMaxPackageName, + getPostureInputHiddenVars, + getPosturePolicy, + getCspmCloudShellDefaultValue, +} from './utils'; import { getMockPolicyAWS, getMockPolicyK8s, getMockPolicyEKS } from './mocks'; +import type { PackageInfo } from '@kbn/fleet-plugin/common'; describe('getPosturePolicy', () => { for (const [name, getPolicy, expectedVars] of [ @@ -123,3 +129,126 @@ describe('getMaxPackageName', () => { expect(result).toBe('kspm-1'); }); }); + +describe('getCspmCloudShellDefaultValue', () => { + it('should return empty string when policy_templates is missing', () => { + const packagePolicy = { name: 'test' } as PackageInfo; + + const result = getCspmCloudShellDefaultValue(packagePolicy); + + expect(result).toBe(''); + }); + + it('should return empty string when policy_templates.name is not cspm', () => { + const packagePolicy = { name: 'test', policy_templates: [{ name: 'kspm' }] } as PackageInfo; + + const result = getCspmCloudShellDefaultValue(packagePolicy); + + expect(result).toBe(''); + }); + + it('should return empty string when policy_templates.inputs is missing', () => { + const packagePolicy = { name: 'test', policy_templates: [{ name: 'cspm' }] } as PackageInfo; + + const result = getCspmCloudShellDefaultValue(packagePolicy); + + expect(result).toBe(''); + }); + + it('should return empty string when policy_templates.inputs is empty', () => { + const packagePolicy = { + name: 'test', + policy_templates: [ + { + title: '', + description: '', + name: 'cspm', + inputs: [{}], + }, + ], + } as PackageInfo; + + const result = getCspmCloudShellDefaultValue(packagePolicy); + + expect(result).toBe(''); + }); + + it('should return empty string when policy_templates.inputs is undefined', () => { + const packagePolicy = { + name: 'test', + policy_templates: [ + { + title: '', + description: '', + name: 'cspm', + inputs: undefined, + }, + ], + } as PackageInfo; + + const result = getCspmCloudShellDefaultValue(packagePolicy); + + expect(result).toBe(''); + }); + + it('should return empty string when policy_templates.inputs.vars does not have cloud_shell_url', () => { + const packagePolicy = { + name: 'test', + policy_templates: [ + { + title: '', + description: '', + name: 'cspm', + inputs: [{ vars: [{ name: 'cloud_shell_url_FAKE' }] }], + }, + ], + } as PackageInfo; + + const result = getCspmCloudShellDefaultValue(packagePolicy); + + expect(result).toBe(''); + }); + + it('should return empty string when policy_templates.inputs.varshave cloud_shell_url but no default', () => { + const packagePolicy = { + name: 'test', + policy_templates: [ + { + title: '', + description: '', + name: 'cspm', + inputs: [{ vars: [{ name: 'cloud_shell_url' }] }], + }, + ], + } as PackageInfo; + + const result = getCspmCloudShellDefaultValue(packagePolicy); + + expect(result).toBe(''); + }); + + it('should cloud shell url when policy_templates.inputs.vars have cloud_shell_url', () => { + const packagePolicy = { + name: 'test', + policy_templates: [ + { + title: '', + description: '', + name: 'cspm', + inputs: [ + { + vars: [ + { name: 'cloud_shell_url_FAKE', default: 'URL_FAKE' }, + { name: 'cloud_shell_url', default: 'URL' }, + ], + }, + ], + }, + ], + } as PackageInfo; + + const result = getCspmCloudShellDefaultValue(packagePolicy); + + expect(result).toBe('URL'); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts index 6376f2b828670..7d4233b8016df 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts @@ -207,6 +207,7 @@ export const getPolicyTemplateInputOptions = (policyTemplate: CloudSecurityPolic label: o.name, icon: o.icon, disabled: o.disabled, + isBeta: o.isBeta, })); export const getMaxPackageName = ( From dc777e6adbcbe67593236bf7db886e5785fb1e2e Mon Sep 17 00:00:00 2001 From: Ricky Ang Date: Thu, 10 Aug 2023 15:52:51 -0700 Subject: [PATCH 14/18] small bug fix --- .../components/fleet_extensions/gcp_credential_form.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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_credential_form.tsx index 9d699ea31107f..88f6ff77fde21 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_credential_form.tsx @@ -335,8 +335,8 @@ export const GcpCredentialsForm = ({ updatePolicy( getPosturePolicy(newPolicy, input.type, { setup_access: { - // Restoring last manual credentials type or defaulting to the first option - value: lastSetupAccessType.current || SETUP_ACCESS_MANUAL, + // Restoring last manual credentials type + value: SETUP_ACCESS_MANUAL, type: 'text', }, // Restoring fields from manual setup format if any From 34a5808917d465cc58a70d350fe051d66c79197c Mon Sep 17 00:00:00 2001 From: Ricky Ang Date: Thu, 10 Aug 2023 23:52:20 -0700 Subject: [PATCH 15/18] PR comments --- .../components/fleet_extensions/csp_boxed_radio_group.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/csp_boxed_radio_group.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/csp_boxed_radio_group.tsx index c1305aeafd7ff..c9ba38eccb2e6 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/csp_boxed_radio_group.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/csp_boxed_radio_group.tsx @@ -58,9 +58,7 @@ export const RadioGroup = ({ content={option.tooltip} anchorProps={{ style: { - flexGrow: 1, - /* Set Maximum width ONLY for Integration type buttons, we know this because only Integration Type has icons */ - ...(option.icon && { maxWidth: `calc(${euiTheme.size.xxs} * 115)` }), + flex: '1 1 0', }, }} > From 0138c7df6c97e806ecfc65129fa29b14a3aff27e Mon Sep 17 00:00:00 2001 From: Ricky Ang Date: Mon, 14 Aug 2023 09:38:48 -0700 Subject: [PATCH 16/18] added google cloud logo --- .../public/assets/icons/google_cloud_logo.svg | 13 +++++++++++++ .../public/common/constants.ts | 5 +++-- .../public/components/cis_benchmark_icon.tsx | 3 ++- 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 x-pack/plugins/cloud_security_posture/public/assets/icons/google_cloud_logo.svg diff --git a/x-pack/plugins/cloud_security_posture/public/assets/icons/google_cloud_logo.svg b/x-pack/plugins/cloud_security_posture/public/assets/icons/google_cloud_logo.svg new file mode 100644 index 0000000000000..c6eb8b6752f4a --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/assets/icons/google_cloud_logo.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/x-pack/plugins/cloud_security_posture/public/common/constants.ts b/x-pack/plugins/cloud_security_posture/public/common/constants.ts index 6ef38730dcf34..57cefa02344ea 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/constants.ts @@ -27,6 +27,7 @@ import { import eksLogo from '../assets/icons/cis_eks_logo.svg'; import aksLogo from '../assets/icons/cis_aks_logo.svg'; import gkeLogo from '../assets/icons/cis_gke_logo.svg'; +import googleCloudLogo from '../assets/icons/google_cloud_logo.svg'; export const statusColors = { passed: euiThemeVars.euiColorSuccess, @@ -91,7 +92,7 @@ export const cloudPostureIntegrations: CloudPostureIntegrations = { benchmark: i18n.translate('xpack.csp.cspmIntegration.gcpOption.benchmarkTitle', { defaultMessage: 'CIS GCP', }), - icon: 'logoGCP', + icon: googleCloudLogo, isBeta: true, }, { @@ -191,7 +192,7 @@ export const cloudPostureIntegrations: CloudPostureIntegrations = { defaultMessage: 'GCP', }), disabled: true, - icon: 'logoGCP', + icon: googleCloudLogo, tooltip: i18n.translate('xpack.csp.vulnMgmtIntegration.gcpOption.tooltipContent', { defaultMessage: 'Coming soon', }), diff --git a/x-pack/plugins/cloud_security_posture/public/components/cis_benchmark_icon.tsx b/x-pack/plugins/cloud_security_posture/public/components/cis_benchmark_icon.tsx index 6ca379ffb24a4..a3cb09bcc4912 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/cis_benchmark_icon.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/cis_benchmark_icon.tsx @@ -10,6 +10,7 @@ import { EuiIcon, EuiToolTip } from '@elastic/eui'; import { CSSInterpolation } from '@emotion/serialize'; import type { BenchmarkId } from '../../common/types'; import cisEksIcon from '../assets/icons/cis_eks_logo.svg'; +import googleCloudLogo from '../assets/icons/google_cloud_logo.svg'; interface Props { type: BenchmarkId; @@ -24,7 +25,7 @@ const getBenchmarkIdIconType = (props: Props): string => { case 'cis_aws': return 'logoAWS'; case 'cis_gcp': - return 'logoGCP'; + return googleCloudLogo; case 'cis_k8s': default: return 'logoKubernetes'; From df140f6ae80a56841f468c75d3e183b4cf9b9811 Mon Sep 17 00:00:00 2001 From: Ricky Ang Date: Mon, 14 Aug 2023 12:39:40 -0700 Subject: [PATCH 17/18] pr comments --- .../public/assets/icons/google_cloud_logo.svg | 25 ++++++++++--------- .../components/accounts_evaluated_widget.tsx | 10 ++++---- .../public/components/cis_benchmark_icon.tsx | 5 ++-- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/assets/icons/google_cloud_logo.svg b/x-pack/plugins/cloud_security_posture/public/assets/icons/google_cloud_logo.svg index c6eb8b6752f4a..5dc6ff781d86f 100644 --- a/x-pack/plugins/cloud_security_posture/public/assets/icons/google_cloud_logo.svg +++ b/x-pack/plugins/cloud_security_posture/public/assets/icons/google_cloud_logo.svg @@ -1,13 +1,14 @@ - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx b/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx index c912a61224757..f52108306a143 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx @@ -38,12 +38,12 @@ export const AccountsEvaluatedWidget = ({ return ( <> - + {cisAwsClusterAmount > 0 && ( - + - + 0 && ( - + - + { @@ -34,6 +35,6 @@ const getBenchmarkIdIconType = (props: Props): string => { export const CISBenchmarkIcon = (props: Props) => ( - + ); From bf534ec43ba2fbff5d2f41428e562eee18c1f040 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 14 Aug 2023 19:43:58 +0000 Subject: [PATCH 18/18] [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' --- .../public/components/accounts_evaluated_widget.tsx | 4 ++-- .../public/components/cis_benchmark_icon.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx b/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx index f52108306a143..490df0c3d9215 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx @@ -43,7 +43,7 @@ export const AccountsEvaluatedWidget = ({ - + - + { export const CISBenchmarkIcon = (props: Props) => ( - + );