[EDR Workflows] Artifact Rollout - feature flag on, copy changes (#167489)

https://github.com/elastic/security-team/issues/7442

This PR:
1. Sets `protectionUpdatesEnabled` feature flag to `true`.
2. Text changes requested
[here](https://github.com/elastic/security-team/issues/7442)
3. Introduces ~~dismissable~~ callout that warns user about consequences
of turning auto updates off.
4. Adds cypress test that validates note persistance.

![Screenshot 2023-09-29 at 10 37
40](6879cc8c-beb9-4f0e-a378-357ae96ee80b)
![Screenshot 2023-09-29 at 10 37
17](48d2df33-9e9a-4b07-a589-ddd7e904e07b)
![Screenshot 2023-09-29 at 10 37
25](5691cc54-5b4e-4d32-a5a1-42ef801ba491)
This commit is contained in:
Konrad Szwarc 2023-09-29 16:59:05 +02:00 committed by GitHub
parent fbd820b6c6
commit 41cf85bee7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 66 additions and 17 deletions

View file

@ -108,7 +108,7 @@ export const allowedExperimentalValues = Object.freeze({
/** /**
* Enables Protection Updates tab in the Endpoint Policy Details page * Enables Protection Updates tab in the Endpoint Policy Details page
*/ */
protectionUpdatesEnabled: false, protectionUpdatesEnabled: true,
}); });
type ExperimentalConfigKeys = Array<keyof ExperimentalFeatures>; type ExperimentalConfigKeys = Array<keyof ExperimentalFeatures>;

View file

@ -20,7 +20,6 @@ describe(
'Policy Details', 'Policy Details',
{ {
tags: '@ess', tags: '@ess',
env: { ftrConfig: { enableExperimental: ['protectionUpdatesEnabled'] } },
}, },
() => { () => {
describe('Protection updates', () => { describe('Protection updates', () => {
@ -57,10 +56,10 @@ describe(
it('should render the protection updates tab content', () => { it('should render the protection updates tab content', () => {
loadProtectionUpdatesUrl(policy.id); loadProtectionUpdatesUrl(policy.id);
cy.getByTestSubj('protection-updates-warning-callout');
cy.getByTestSubj('protection-updates-automatic-updates-enabled'); cy.getByTestSubj('protection-updates-automatic-updates-enabled');
cy.getByTestSubj('protection-updates-manifest-switch'); cy.getByTestSubj('protection-updates-manifest-switch');
cy.getByTestSubj('protection-updates-manifest-name-title'); cy.getByTestSubj('protection-updates-manifest-name-title');
cy.getByTestSubj('protection-updates-manifest-name');
cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled');
cy.getByTestSubj('protection-updates-manifest-switch').click(); cy.getByTestSubj('protection-updates-manifest-switch').click();
@ -201,6 +200,11 @@ describe(
cy.getByTestSubj('protection-updates-manifest-note').contains(updatedTestNote); cy.getByTestSubj('protection-updates-manifest-note').contains(updatedTestNote);
cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled');
}); });
it('should preserve note', () => {
loadProtectionUpdatesUrl(policy.id);
cy.getByTestSubj('protection-updates-manifest-note').contains(updatedTestNote);
});
}); });
describe('Renders read only protection updates for user without write permissions', () => { describe('Renders read only protection updates for user without write permissions', () => {
@ -238,7 +242,6 @@ describe(
cy.getByTestSubj('protection-updates-manifest-switch').should('not.exist'); cy.getByTestSubj('protection-updates-manifest-switch').should('not.exist');
cy.getByTestSubj('protection-updates-state-view-mode'); cy.getByTestSubj('protection-updates-state-view-mode');
cy.getByTestSubj('protection-updates-manifest-name-title'); cy.getByTestSubj('protection-updates-manifest-name-title');
cy.getByTestSubj('protection-updates-manifest-name');
cy.getByTestSubj('protection-updates-manifest-name-deployed-version-title'); cy.getByTestSubj('protection-updates-manifest-name-deployed-version-title');
cy.getByTestSubj('protection-updates-deployed-version').contains( cy.getByTestSubj('protection-updates-deployed-version').contains(

View file

@ -12,7 +12,8 @@ import type { IndexedFleetEndpointPolicyResponse } from '../../../../../common/e
import { login } from '../../tasks/login'; import { login } from '../../tasks/login';
import { createAgentPolicyTask, getEndpointIntegrationVersion } from '../../tasks/fleet'; import { createAgentPolicyTask, getEndpointIntegrationVersion } from '../../tasks/fleet';
describe('Disabled experimental features on: ', { tags: '@ess' }, () => { // We need a way to disable experimental features in the Cypress tests
describe.skip('Disabled experimental features on: ', { tags: '@ess' }, () => {
describe('Policy list', () => { describe('Policy list', () => {
describe('Renders policy list without protection updates feature flag', () => { describe('Renders policy list without protection updates feature flag', () => {
let indexedPolicy: IndexedFleetEndpointPolicyResponse; let indexedPolicy: IndexedFleetEndpointPolicyResponse;

View file

@ -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 { EuiCallOut, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import React from 'react';
export const ProtectionUpdatesWarningPanel = () => (
<EuiCallOut
title={i18n.translate('xpack.securitySolution.endpoint.protectionUpdates.warningPanel.title', {
defaultMessage: 'Attention',
})}
iconType="alert"
data-test-subj="protection-updates-warning-callout"
>
<FormattedMessage
id="xpack.securitySolution.endpoint.protectionUpdates.warningPanel.content"
defaultMessage="Elastic provides periodic updates to protections artifacts such as the global exception list, malware models, and rule packages to ensure your environment is up to date with latest protections. By default, these artifacts are updated automatically. Disable the automatic updates toggle to manually manage updates to the protections artifacts."
/>
<EuiSpacer size="s" />
<FormattedMessage
id="xpack.securitySolution.endpoint.protectionUpdates.warningPanel.content.note"
defaultMessage="{note} It is strongly advised to keep automatic updates enabled to ensure the highest level of security for your environment. Proceed with caution if you decide to disable automatic updates."
values={{
note: (
<strong>
{i18n.translate(
'xpack.securitySolution.endpoint.protectionUpdates.warningPanel.content.note.bold',
{
defaultMessage: 'Note:',
}
)}
</strong>
),
}}
/>
</EuiCallOut>
);

View file

@ -36,6 +36,7 @@ import { useUserPrivileges } from '../../../../../common/components/user_privile
import { useToasts } from '../../../../../common/lib/kibana'; import { useToasts } from '../../../../../common/lib/kibana';
import { useUpdateEndpointPolicy } from '../../../../hooks/policy/use_update_endpoint_policy'; import { useUpdateEndpointPolicy } from '../../../../hooks/policy/use_update_endpoint_policy';
import type { PolicyData, MaybeImmutable } from '../../../../../../common/endpoint/types'; import type { PolicyData, MaybeImmutable } from '../../../../../../common/endpoint/types';
import { ProtectionUpdatesWarningPanel } from './components/protection_updates_warning_panel';
interface ProtectionUpdatesLayoutProps { interface ProtectionUpdatesLayoutProps {
policy: MaybeImmutable<PolicyData>; policy: MaybeImmutable<PolicyData>;
@ -44,14 +45,14 @@ interface ProtectionUpdatesLayoutProps {
const AUTOMATIC_UPDATES_CHECKBOX_LABEL = i18n.translate( const AUTOMATIC_UPDATES_CHECKBOX_LABEL = i18n.translate(
'xpack.securitySolution.endpoint.protectionUpdates.useAutomaticUpdates', 'xpack.securitySolution.endpoint.protectionUpdates.useAutomaticUpdates',
{ {
defaultMessage: 'Use automatic updates', defaultMessage: 'Automatic updates enabled',
} }
); );
const AUTOMATIC_UPDATES_OFF_CHECKBOX_LABEL = i18n.translate( const AUTOMATIC_UPDATES_OFF_CHECKBOX_LABEL = i18n.translate(
'xpack.securitySolution.endpoint.protectionUpdates.useAutomaticUpdatesOff', 'xpack.securitySolution.endpoint.protectionUpdates.useAutomaticUpdatesOff',
{ {
defaultMessage: "Don't use automatic updates", defaultMessage: 'Automatic updates disabled.',
} }
); );
@ -373,23 +374,22 @@ export const ProtectionUpdatesLayout = React.memo<ProtectionUpdatesLayoutProps>(
<EuiFlexItem grow={1}> <EuiFlexItem grow={1}>
<EuiTitle size="xxs" data-test-subj={'protection-updates-manifest-name-title'}> <EuiTitle size="xxs" data-test-subj={'protection-updates-manifest-name-title'}>
<h5> <h5>
{i18n.translate( {i18n.translate('xpack.securitySolution.endpoint.protectionUpdates.title', {
'xpack.securitySolution.endpoint.protectionUpdates.manifestName', defaultMessage: 'Manage protection updates',
{ })}
defaultMessage: 'Manifest name',
}
)}
</h5> </h5>
</EuiTitle> </EuiTitle>
<EuiText size="m" data-test-subj="protection-updates-manifest-name">
{'artifactsec'}
</EuiText>
</EuiFlexItem> </EuiFlexItem>
<EuiShowFor sizes={['l', 'xl', 'm']}> <EuiShowFor sizes={['l', 'xl', 'm']}>
{canWritePolicyManagement ? ( {canWritePolicyManagement ? (
<EuiSwitch <EuiSwitch
disabled={isUpdating || createNoteInProgress || getNoteInProgress} disabled={isUpdating || createNoteInProgress || getNoteInProgress}
label={'Update manifest automatically'} label={i18n.translate(
'xpack.securitySolution.endpoint.protectionUpdates.enableAutomaticUpdates',
{
defaultMessage: 'Enable automatic updates',
}
)}
labelProps={{ 'data-test-subj': 'protection-updates-manifest-switch-label' }} labelProps={{ 'data-test-subj': 'protection-updates-manifest-switch-label' }}
checked={automaticUpdatesEnabled} checked={automaticUpdatesEnabled}
onChange={toggleAutomaticUpdates} onChange={toggleAutomaticUpdates}
@ -406,6 +406,8 @@ export const ProtectionUpdatesLayout = React.memo<ProtectionUpdatesLayoutProps>(
<EuiHorizontalRule margin="m" /> <EuiHorizontalRule margin="m" />
<EuiSpacer size="m" /> <EuiSpacer size="m" />
<div style={{ padding: `0 ${paddingSize} ${paddingSize} ${paddingSize}` }}> <div style={{ padding: `0 ${paddingSize} ${paddingSize} ${paddingSize}` }}>
<ProtectionUpdatesWarningPanel />
<EuiSpacer size="m" />
{renderManifestOutdatedCallOut()} {renderManifestOutdatedCallOut()}
{renderContent()} {renderContent()}
</div> </div>