diff --git a/x-pack/plugins/cloud_security_posture/common/types_old.ts b/x-pack/plugins/cloud_security_posture/common/types_old.ts index f77ac4678a526..19e18902b7ea1 100644 --- a/x-pack/plugins/cloud_security_posture/common/types_old.ts +++ b/x-pack/plugins/cloud_security_posture/common/types_old.ts @@ -133,6 +133,7 @@ export interface BaseCspSetupStatus { vuln_mgmt: BaseCspSetupBothPolicy; isPluginInitialized: boolean; installedPackageVersion?: string | undefined; + hasMisconfigurationsFindings?: boolean; } export type CspSetupStatus = BaseCspSetupStatus; diff --git a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_subscription_status.ts b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_is_subscription_status_valid.ts similarity index 94% rename from x-pack/plugins/cloud_security_posture/public/common/hooks/use_subscription_status.ts rename to x-pack/plugins/cloud_security_posture/public/common/hooks/use_is_subscription_status_valid.ts index f8bda84dbcb65..99ded40b04f63 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_subscription_status.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_is_subscription_status_valid.ts @@ -12,9 +12,10 @@ import { useKibana } from './use_kibana'; const SUBSCRIPTION_QUERY_KEY = 'csp_subscription_query_key'; -export const useSubscriptionStatus = () => { +export const useIsSubscriptionStatusValid = () => { const { licensing } = useKibana().services; const { isCloudEnabled } = useContext(SetupContext); + return useQuery([SUBSCRIPTION_QUERY_KEY], async () => { const license = await licensing.refresh(); return isSubscriptionAllowed(isCloudEnabled, license); diff --git a/x-pack/plugins/cloud_security_posture/public/components/cloud_posture_page.test.tsx b/x-pack/plugins/cloud_security_posture/public/components/cloud_posture_page.test.tsx index 05fedd4d8adec..22fc6b4ae65f8 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/cloud_posture_page.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/cloud_posture_page.test.tsx @@ -5,7 +5,6 @@ * 2.0. */ -import { useSubscriptionStatus } from '../common/hooks/use_subscription_status'; import Chance from 'chance'; import { DEFAULT_NO_DATA_TEST_SUBJECT, @@ -13,7 +12,6 @@ import { isCommonError, LOADING_STATE_TEST_SUBJECT, PACKAGE_NOT_INSTALLED_TEST_SUBJECT, - SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT, } from './cloud_posture_page'; import { createReactQueryResponse } from '../test/fixtures/react_query'; import { TestProvider } from '../test/test_provider'; @@ -23,27 +21,17 @@ import React, { ComponentProps } from 'react'; import { UseQueryResult } from '@tanstack/react-query'; import { CloudPosturePage } from './cloud_posture_page'; import { NoDataPage } from '@kbn/kibana-react-plugin/public'; -import { useLicenseManagementLocatorApi } from '../common/api/use_license_management_locator_api'; const chance = new Chance(); jest.mock('../common/api/use_setup_status_api'); jest.mock('../common/api/use_license_management_locator_api'); -jest.mock('../common/hooks/use_subscription_status'); +jest.mock('../common/hooks/use_is_subscription_status_valid'); jest.mock('../common/navigation/use_csp_integration_link'); describe('', () => { beforeEach(() => { jest.resetAllMocks(); - - (useSubscriptionStatus as jest.Mock).mockImplementation(() => - createReactQueryResponse({ - status: 'success', - data: true, - }) - ); - - (useLicenseManagementLocatorApi as jest.Mock).mockImplementation(undefined); }); const renderCloudPosturePage = ( @@ -72,101 +60,16 @@ describe('', () => { ); }; - it('renders with license url locator', () => { - (useSubscriptionStatus as jest.Mock).mockImplementation(() => - createReactQueryResponse({ - status: 'success', - data: false, - }) - ); - - (useLicenseManagementLocatorApi as jest.Mock).mockImplementation(() => 'http://license-url'); - - renderCloudPosturePage(); - - expect(screen.getByTestId('has_locator')).toBeInTheDocument(); - }); - - it('renders no license url locator', () => { - (useSubscriptionStatus as jest.Mock).mockImplementation(() => - createReactQueryResponse({ - status: 'success', - data: false, - }) - ); - - (useLicenseManagementLocatorApi as jest.Mock).mockImplementation(undefined); - - renderCloudPosturePage(); - expect(screen.getByTestId('no_locator')).toBeInTheDocument(); - }); - it('renders children if setup status is indexed', () => { const children = chance.sentence(); renderCloudPosturePage({ children }); expect(screen.getByText(children)).toBeInTheDocument(); expect(screen.queryByTestId(LOADING_STATE_TEST_SUBJECT)).not.toBeInTheDocument(); - expect(screen.queryByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).not.toBeInTheDocument(); expect(screen.queryByTestId(ERROR_STATE_TEST_SUBJECT)).not.toBeInTheDocument(); expect(screen.queryByTestId(PACKAGE_NOT_INSTALLED_TEST_SUBJECT)).not.toBeInTheDocument(); }); - it('renders default loading state when the subscription query is loading', () => { - (useSubscriptionStatus as jest.Mock).mockImplementation( - () => - createReactQueryResponse({ - status: 'loading', - }) as unknown as UseQueryResult - ); - - const children = chance.sentence(); - renderCloudPosturePage({ children }); - - expect(screen.getByTestId(LOADING_STATE_TEST_SUBJECT)).toBeInTheDocument(); - expect(screen.queryByText(children)).not.toBeInTheDocument(); - expect(screen.queryByTestId(ERROR_STATE_TEST_SUBJECT)).not.toBeInTheDocument(); - expect(screen.queryByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).not.toBeInTheDocument(); - expect(screen.queryByTestId(PACKAGE_NOT_INSTALLED_TEST_SUBJECT)).not.toBeInTheDocument(); - }); - - it('renders default error state when the subscription query has an error', () => { - (useSubscriptionStatus as jest.Mock).mockImplementation( - () => - createReactQueryResponse({ - status: 'error', - error: new Error('error'), - }) as unknown as UseQueryResult - ); - - const children = chance.sentence(); - renderCloudPosturePage({ children }); - - expect(screen.getByTestId(ERROR_STATE_TEST_SUBJECT)).toBeInTheDocument(); - expect(screen.queryByTestId(LOADING_STATE_TEST_SUBJECT)).not.toBeInTheDocument(); - expect(screen.queryByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).not.toBeInTheDocument(); - expect(screen.queryByText(children)).not.toBeInTheDocument(); - expect(screen.queryByTestId(PACKAGE_NOT_INSTALLED_TEST_SUBJECT)).not.toBeInTheDocument(); - }); - - it('renders subscription not allowed prompt if subscription is not installed', () => { - (useSubscriptionStatus as jest.Mock).mockImplementation(() => - createReactQueryResponse({ - status: 'success', - data: false, - }) - ); - - const children = chance.sentence(); - renderCloudPosturePage({ children }); - - expect(screen.queryByTestId(PACKAGE_NOT_INSTALLED_TEST_SUBJECT)).not.toBeInTheDocument(); - expect(screen.queryByText(children)).not.toBeInTheDocument(); - expect(screen.queryByTestId(LOADING_STATE_TEST_SUBJECT)).not.toBeInTheDocument(); - expect(screen.getByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).toBeInTheDocument(); - expect(screen.queryByTestId(ERROR_STATE_TEST_SUBJECT)).not.toBeInTheDocument(); - }); - it('renders default loading text when query isLoading', () => { const query = createReactQueryResponse({ status: 'loading', @@ -176,7 +79,6 @@ describe('', () => { renderCloudPosturePage({ children, query }); expect(screen.getByTestId(LOADING_STATE_TEST_SUBJECT)).toBeInTheDocument(); - expect(screen.queryByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).not.toBeInTheDocument(); expect(screen.queryByText(children)).not.toBeInTheDocument(); expect(screen.queryByTestId(ERROR_STATE_TEST_SUBJECT)).not.toBeInTheDocument(); expect(screen.queryByTestId(PACKAGE_NOT_INSTALLED_TEST_SUBJECT)).not.toBeInTheDocument(); @@ -191,7 +93,6 @@ describe('', () => { renderCloudPosturePage({ children, query }); expect(screen.getByTestId(LOADING_STATE_TEST_SUBJECT)).toBeInTheDocument(); - expect(screen.queryByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).not.toBeInTheDocument(); expect(screen.queryByText(children)).not.toBeInTheDocument(); expect(screen.queryByTestId(ERROR_STATE_TEST_SUBJECT)).not.toBeInTheDocument(); expect(screen.queryByTestId(PACKAGE_NOT_INSTALLED_TEST_SUBJECT)).not.toBeInTheDocument(); @@ -220,7 +121,6 @@ describe('', () => { expect(screen.getByText(text, { exact: false })).toBeInTheDocument() ); expect(screen.getByTestId(ERROR_STATE_TEST_SUBJECT)).toBeInTheDocument(); - expect(screen.queryByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).not.toBeInTheDocument(); expect(screen.queryByTestId(LOADING_STATE_TEST_SUBJECT)).not.toBeInTheDocument(); expect(screen.queryByText(children)).not.toBeInTheDocument(); expect(screen.queryByTestId(PACKAGE_NOT_INSTALLED_TEST_SUBJECT)).not.toBeInTheDocument(); @@ -253,7 +153,6 @@ describe('', () => { [error, statusCode].forEach((text) => expect(screen.queryByText(text)).not.toBeInTheDocument()); expect(screen.queryByTestId(ERROR_STATE_TEST_SUBJECT)).not.toBeInTheDocument(); expect(screen.queryByTestId(LOADING_STATE_TEST_SUBJECT)).not.toBeInTheDocument(); - expect(screen.queryByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).not.toBeInTheDocument(); expect(screen.queryByText(children)).not.toBeInTheDocument(); expect(screen.queryByTestId(PACKAGE_NOT_INSTALLED_TEST_SUBJECT)).not.toBeInTheDocument(); }); @@ -275,7 +174,6 @@ describe('', () => { expect(screen.getByText(loading)).toBeInTheDocument(); expect(screen.queryByTestId(ERROR_STATE_TEST_SUBJECT)).not.toBeInTheDocument(); expect(screen.queryByTestId(LOADING_STATE_TEST_SUBJECT)).not.toBeInTheDocument(); - expect(screen.queryByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).not.toBeInTheDocument(); expect(screen.queryByText(children)).not.toBeInTheDocument(); expect(screen.queryByTestId(PACKAGE_NOT_INSTALLED_TEST_SUBJECT)).not.toBeInTheDocument(); }); @@ -291,7 +189,6 @@ describe('', () => { expect(screen.getByTestId(DEFAULT_NO_DATA_TEST_SUBJECT)).toBeInTheDocument(); expect(screen.queryByTestId(LOADING_STATE_TEST_SUBJECT)).not.toBeInTheDocument(); - expect(screen.queryByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).not.toBeInTheDocument(); expect(screen.queryByText(children)).not.toBeInTheDocument(); expect(screen.queryByTestId(ERROR_STATE_TEST_SUBJECT)).not.toBeInTheDocument(); expect(screen.queryByTestId(PACKAGE_NOT_INSTALLED_TEST_SUBJECT)).not.toBeInTheDocument(); @@ -320,7 +217,6 @@ describe('', () => { expect(screen.getByText(pageTitle)).toBeInTheDocument(); expect(screen.getAllByText(solution, { exact: false })[0]).toBeInTheDocument(); expect(screen.queryByTestId(LOADING_STATE_TEST_SUBJECT)).not.toBeInTheDocument(); - expect(screen.queryByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).not.toBeInTheDocument(); expect(screen.queryByText(children)).not.toBeInTheDocument(); expect(screen.queryByTestId(ERROR_STATE_TEST_SUBJECT)).not.toBeInTheDocument(); expect(screen.queryByTestId(PACKAGE_NOT_INSTALLED_TEST_SUBJECT)).not.toBeInTheDocument(); diff --git a/x-pack/plugins/cloud_security_posture/public/components/cloud_posture_page.tsx b/x-pack/plugins/cloud_security_posture/public/components/cloud_posture_page.tsx index b2f1d892da907..34ea821ed2620 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/cloud_posture_page.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/cloud_posture_page.tsx @@ -11,8 +11,6 @@ import { EuiEmptyPrompt } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { NoDataPage, NoDataPageProps } from '@kbn/kibana-react-plugin/public'; import { css } from '@emotion/react'; -import { SubscriptionNotAllowed } from './subscription_not_allowed'; -import { useSubscriptionStatus } from '../common/hooks/use_subscription_status'; import { FullSizeCenteredPage } from './full_size_centered_page'; import { CspLoadingState } from './csp_loading_state'; @@ -22,7 +20,6 @@ export const PACKAGE_NOT_INSTALLED_TEST_SUBJECT = 'cloud_posture_page_package_no export const CSPM_INTEGRATION_NOT_INSTALLED_TEST_SUBJECT = 'cloud_posture_page_cspm_not_installed'; export const KSPM_INTEGRATION_NOT_INSTALLED_TEST_SUBJECT = 'cloud_posture_page_kspm_not_installed'; export const DEFAULT_NO_DATA_TEST_SUBJECT = 'cloud_posture_page_no_data'; -export const SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT = 'cloud_posture_page_subscription_not_allowed'; interface CommonError { body: { @@ -150,12 +147,6 @@ export const defaultNoDataRenderer = () => ( ); -const subscriptionNotAllowedRenderer = () => ( - - - -); - interface CloudPosturePageProps { children: React.ReactNode; query?: UseQueryResult; @@ -171,21 +162,7 @@ export const CloudPosturePage = ({ errorRender = defaultErrorRenderer, noDataRenderer = defaultNoDataRenderer, }: CloudPosturePageProps) => { - const subscriptionStatus = useSubscriptionStatus(); - const render = () => { - if (subscriptionStatus.isError) { - return defaultErrorRenderer(subscriptionStatus.error); - } - - if (subscriptionStatus.isLoading) { - return defaultLoadingRenderer(); - } - - if (!subscriptionStatus.data) { - return subscriptionNotAllowedRenderer(); - } - if (!query) { return children; } 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 116170b2ac29c..76134c4d41df0 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 @@ -5,7 +5,7 @@ * 2.0. */ import React from 'react'; -import { render, waitFor, within } from '@testing-library/react'; +import { render, screen, waitFor, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { CspPolicyTemplateForm, @@ -53,9 +53,12 @@ import { GCP_CREDENTIALS_TYPE_OPTIONS_TEST_SUBJ, SETUP_TECHNOLOGY_SELECTOR_ACCORDION_TEST_SUBJ, SETUP_TECHNOLOGY_SELECTOR_TEST_SUBJ, + SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT, } from '../test_subjects'; import { ExperimentalFeaturesService } from '@kbn/fleet-plugin/public/services'; import { createFleetTestRendererMock } from '@kbn/fleet-plugin/public/mock'; +import { useIsSubscriptionStatusValid } from '../../common/hooks/use_is_subscription_status_valid'; +import { useLicenseManagementLocatorApi } from '../../common/api/use_license_management_locator_api'; // mock useParams jest.mock('react-router-dom', () => ({ @@ -66,6 +69,8 @@ jest.mock('react-router-dom', () => ({ })); jest.mock('../../common/api/use_setup_status_api'); jest.mock('../../common/api/use_package_policy_list'); +jest.mock('../../common/hooks/use_is_subscription_status_valid'); +jest.mock('../../common/api/use_license_management_locator_api'); jest.mock('@kbn/fleet-plugin/public/services/experimental_features'); const onChange = jest.fn(); @@ -85,9 +90,11 @@ describe('', () => { (useParams as jest.Mock).mockReturnValue({ integration: undefined, }); + mockedExperimentalFeaturesService.get.mockReturnValue({ secretsStorage: true, } as any); + (usePackagePolicyList as jest.Mock).mockImplementation((packageName) => createReactQueryResponseWithRefetch({ status: 'success', @@ -96,13 +103,22 @@ describe('', () => { }, }) ); + onChange.mockClear(); + (useCspSetupStatusApi as jest.Mock).mockImplementation(() => createReactQueryResponseWithRefetch({ status: 'success', data: { status: 'indexed', installedPackageVersion: '1.2.13' }, }) ); + + (useIsSubscriptionStatusValid as jest.Mock).mockImplementation(() => + createReactQueryResponse({ + status: 'success', + data: true, + }) + ); }); const WrappedComponent = ({ @@ -145,6 +161,53 @@ describe('', () => { ); }; + it('shows license block if subscription is not allowed', () => { + (useIsSubscriptionStatusValid as jest.Mock).mockImplementation(() => + createReactQueryResponse({ + status: 'success', + data: false, + }) + ); + + const policy = getMockPolicyK8s(); + const { rerender } = render(); + + rerender(); + expect(screen.getByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).toBeInTheDocument(); + }); + + it('license block renders with license url locator', () => { + (useIsSubscriptionStatusValid as jest.Mock).mockImplementation(() => + createReactQueryResponse({ + status: 'success', + data: false, + }) + ); + (useLicenseManagementLocatorApi as jest.Mock).mockImplementation(() => 'http://license-url'); + + const policy = getMockPolicyK8s(); + const { rerender } = render(); + + rerender(); + expect(screen.getByTestId('has_locator')).toBeInTheDocument(); + }); + + it('license block renders without license url locator', () => { + (useIsSubscriptionStatusValid as jest.Mock).mockImplementation(() => + createReactQueryResponse({ + status: 'success', + data: false, + }) + ); + (useLicenseManagementLocatorApi as jest.Mock).mockImplementation(undefined); + + const policy = getMockPolicyK8s(); + const { rerender } = render(); + + rerender(); + expect(screen.getByTestId('no_locator')).toBeInTheDocument(); + }); + it('updates package policy namespace to default when it changes', () => { const policy = getMockPolicyK8s(); const { rerender } = render(); 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 6a7d73868ced6..f70fb3a18f690 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 @@ -30,6 +30,8 @@ import type { import { PackageInfo, PackagePolicy } from '@kbn/fleet-plugin/common'; import { useParams } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; +import { useIsSubscriptionStatusValid } from '../../common/hooks/use_is_subscription_status_valid'; +import { SubscriptionNotAllowed } from '../subscription_not_allowed'; import { CspRadioGroupProps, RadioGroup } from './csp_boxed_radio_group'; import { assert } from '../../../common/utils/helpers'; import type { CloudSecurityPolicyTemplate, PostureInput } from '../../../common/types_old'; @@ -67,6 +69,7 @@ import { SetupTechnologySelector } from './setup_technology_selector/setup_techn import { useSetupTechnology } from './setup_technology_selector/use_setup_technology'; import { AZURE_CREDENTIALS_TYPE } from './azure_credentials_form/azure_credentials_form'; import { AWS_CREDENTIALS_TYPE } from './aws_credentials_form/aws_credentials_form'; +import { useKibana } from '../../common/hooks/use_kibana'; const DEFAULT_INPUT_TYPE = { kspm: CLOUDBEAT_VANILLA, @@ -537,6 +540,125 @@ const IntegrationSettings = ({ onChange, fields }: IntegrationInfoFieldsProps) = ); +const useEnsureDefaultNamespace = ({ + newPolicy, + input, + updatePolicy, +}: { + newPolicy: NewPackagePolicy; + input: NewPackagePolicyPostureInput; + updatePolicy: (policy: NewPackagePolicy) => void; +}) => { + useEffect(() => { + if (newPolicy.namespace === POSTURE_NAMESPACE) return; + + const policy = { ...getPosturePolicy(newPolicy, input.type), namespace: POSTURE_NAMESPACE }; + updatePolicy(policy); + }, [newPolicy, input, updatePolicy]); +}; + +const usePolicyTemplateInitialName = ({ + isEditPage, + isLoading, + integration, + newPolicy, + packagePolicyList, + updatePolicy, + setCanFetchIntegration, +}: { + isEditPage: boolean; + isLoading: boolean; + integration: CloudSecurityPolicyTemplate | undefined; + newPolicy: NewPackagePolicy; + packagePolicyList: PackagePolicy[] | undefined; + updatePolicy: (policy: NewPackagePolicy) => void; + setCanFetchIntegration: (canFetch: boolean) => void; +}) => { + useEffect(() => { + if (!integration) return; + if (isEditPage) return; + if (isLoading) return; + + const packagePolicyListByIntegration = packagePolicyList?.filter( + (policy) => policy?.vars?.posture?.value === integration + ); + + const currentIntegrationName = getMaxPackageName(integration, packagePolicyListByIntegration); + + if (newPolicy.name === currentIntegrationName) { + return; + } + + updatePolicy({ + ...newPolicy, + name: currentIntegrationName, + }); + setCanFetchIntegration(false); + // since this useEffect should only run on initial mount updatePolicy and newPolicy shouldn't re-trigger it + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isLoading, integration, isEditPage, packagePolicyList]); +}; + +const getSelectedOption = ( + options: NewPackagePolicyInput[], + policyTemplate: string = CSPM_POLICY_TEMPLATE +) => { + // Looks for the enabled deployment (aka input). By default, all inputs are disabled. + // Initial state when all inputs are disabled is to choose the first available of the relevant policyTemplate + // Default selected policy template is CSPM + const selectedOption = + options.find((i) => i.enabled) || + options.find((i) => i.policy_template === policyTemplate) || + options[0]; + + assert(selectedOption, 'Failed to determine selected option'); // We can't provide a default input without knowing the policy template + assert(isPostureInput(selectedOption), 'Unknown option: ' + selectedOption.type); + + return selectedOption; +}; + +/** + * Update CloudFormation template and stack name in the Agent Policy + * based on the selected policy template + */ +const useCloudFormationTemplate = ({ + packageInfo, + newPolicy, + updatePolicy, +}: { + packageInfo: PackageInfo; + newPolicy: NewPackagePolicy; + updatePolicy: (policy: NewPackagePolicy) => void; +}) => { + useEffect(() => { + const templateUrl = getVulnMgmtCloudFormationDefaultValue(packageInfo); + + // If the template is not available, do not update the policy + if (templateUrl === '') return; + + const checkCurrentTemplate = newPolicy?.inputs?.find( + (i: any) => i.type === CLOUDBEAT_VULN_MGMT_AWS + )?.config?.cloud_formation_template_url?.value; + + // If the template is already set, do not update the policy + if (checkCurrentTemplate === templateUrl) return; + + updatePolicy?.({ + ...newPolicy, + inputs: newPolicy.inputs.map((input) => { + if (input.type === CLOUDBEAT_VULN_MGMT_AWS) { + return { + ...input, + config: { cloud_formation_template_url: { value: templateUrl } }, + }; + } + return input; + }), + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [newPolicy?.vars?.cloud_formation_template_url, newPolicy, packageInfo]); +}; + export const CspPolicyTemplateForm = memo( ({ newPolicy, @@ -553,7 +675,11 @@ export const CspPolicyTemplateForm = memo