diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a8cc39b7b072b..bf39e9ea13567 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1842,6 +1842,7 @@ x-pack/plugins/osquery @elastic/security-defend-workflows /x-pack/plugins/fleet/public/components/cloud_security_posture @elastic/fleet @elastic/kibana-cloud-security-posture /x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/cloud_security_posture @elastic/fleet @elastic/kibana-cloud-security-posture /x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.* @elastic/fleet @elastic/kibana-cloud-security-posture +/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/cloud_posture_third_party_support_callout.* @elastic/fleet @elastic/kibana-cloud-security-posture /x-pack/plugins/security_solution/public/cloud_security_posture @elastic/kibana-cloud-security-posture /x-pack/test/security_solution_cypress/cypress/e2e/explore/hosts/vulnerabilities_contextual_flyout.cy.ts @elastic/kibana-cloud-security-posture diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/cloud_posture_third_party_support_callout.test.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/cloud_posture_third_party_support_callout.test.tsx new file mode 100644 index 0000000000000..7b238ef49fa2e --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/cloud_posture_third_party_support_callout.test.tsx @@ -0,0 +1,48 @@ +/* + * 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 { render, screen } from '@testing-library/react'; + +import useLocalStorage from 'react-use/lib/useLocalStorage'; + +import type { PackageInfo } from '../../../../../../../../common'; + +import { CloudPostureThirdPartySupportCallout } from './cloud_posture_third_party_support_callout'; + +jest.mock('react-use/lib/useLocalStorage'); + +describe('CloudPostureThirdPartySupportCallout', () => { + const mockPackageInfo = { name: 'wiz' } as PackageInfo; + + beforeEach(() => { + (useLocalStorage as jest.Mock).mockClear(); + }); + + it('renders callout when package is wiz and callout is not dismissed', () => { + (useLocalStorage as jest.Mock).mockReturnValue([false, jest.fn()]); + + render(); + + expect(screen.getByText(/New! Starting from version 1.9/)).toBeInTheDocument(); + }); + + it('does not render callout when package is not wiz', () => { + const nonWizPackageInfo = { name: 'other' } as PackageInfo; + render(); + + expect(screen.queryByText(/New! Starting from version 1.9/)).not.toBeInTheDocument(); + }); + + it('does not render callout when it has been dismissed', () => { + (useLocalStorage as jest.Mock).mockReturnValue([true, jest.fn()]); + + render(); + + expect(screen.queryByText(/New! Starting from version 1.9/)).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/cloud_posture_third_party_support_callout.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/cloud_posture_third_party_support_callout.tsx new file mode 100644 index 0000000000000..6bd4197dc267e --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/cloud_posture_third_party_support_callout.tsx @@ -0,0 +1,42 @@ +/* + * 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 useLocalStorage from 'react-use/lib/useLocalStorage'; +import { EuiCallOut, EuiSpacer } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import type { PackageInfo } from '../../../../../../../../common'; + +const LS_CLOUD_POSTURE_3P_SUPPORT_WIZ_INTEGRATIONS_CALLOUT_KEY = + 'fleet:cloudSecurityPosture:thirdPartySupport:wizIntegrationsCallout'; + +export const CloudPostureThirdPartySupportCallout = ({ + packageInfo, +}: { + packageInfo: PackageInfo; +}) => { + const [userHasDismissedWizCallout, setUserHasDismissedWizCallout] = useLocalStorage( + LS_CLOUD_POSTURE_3P_SUPPORT_WIZ_INTEGRATIONS_CALLOUT_KEY + ); + + if (packageInfo.name !== 'wiz' || userHasDismissedWizCallout) return null; + + return ( + <> + setUserHasDismissedWizCallout(true)} + iconType="cheer" + title={i18n.translate('xpack.fleet.epm.wizIntegration.newFeaturesCallout', { + defaultMessage: + 'New! Starting from version 1.9, ingest vulnerability and misconfiguration findings from Wiz into Elastic. Leverage out-of-the-box contextual investigation and threat-hunting workflows.', + })} + /> + + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx index 9fa6df1e9f8ca..e96e74b1bb96c 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx @@ -42,6 +42,8 @@ import { SideBarColumn } from '../../../components/side_bar_column'; import type { FleetStartServices } from '../../../../../../../plugin'; +import { CloudPostureThirdPartySupportCallout } from '../components/cloud_posture_third_party_support_callout'; + import { Screenshots } from './screenshots'; import { Readme } from './readme'; import { Details } from './details'; @@ -319,6 +321,7 @@ export const OverviewPage: React.FC = memo( )} + {isPrerelease && (