[Security Solution] Make the starting date either 18 months ago or the first date of this feature availability for controlled artifacts (#168229)

## Summary

Updates the date picker logic for controlled artifacts rollout to either
use 18 months ago OR the first date available with this feature (Oct 1,
2023) - whichever is most recent. This will ensure that users don't pick
an earlier date.

<img width="1725" alt="image"
src="48f42814-30fb-4689-ae97-7fa2350b70bf">

### Checklist
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Konrad Szwarc <konrad.szwarc@elastic.co>
This commit is contained in:
Kevin Logan 2023-10-09 11:31:46 -04:00 committed by GitHub
parent a18b523769
commit c61b39f2cb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 57 additions and 34 deletions

View file

@ -0,0 +1,23 @@
/*
* 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 moment from 'moment';
import { getControlledArtifactCutoffDate } from './controlled_artifact_rollout';
describe('Controlled artifact rollout cut off date', () => {
const eighteenMonthsAgo = moment.utc().subtract(18, 'months').add(1, 'day').startOf('day');
it('should return date 18 months ago if start date is older than 18 months', () => {
expect(
getControlledArtifactCutoffDate(moment('2020-10-01T00:00:00Z').utc()).startOf('day')
).toEqual(eighteenMonthsAgo.startOf('day'));
});
it("should return today's date since it is more recent than 18 months", () => {
const today = moment().utc();
expect(getControlledArtifactCutoffDate(today)).toEqual(today);
});
});

View file

@ -0,0 +1,15 @@
/*
* 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 moment from 'moment';
export function getControlledArtifactCutoffDate(
startingDate: moment.Moment = moment('2023-10-01T00:00:00Z').utc()
): moment.Moment {
const eighteenMonthsAgo = moment.utc().subtract(18, 'months').add(1, 'day');
return moment(startingDate).isAfter(eighteenMonthsAgo) ? startingDate : eighteenMonthsAgo;
}

View file

@ -38,9 +38,6 @@ describe(
beforeEach(() => {
login();
disableExpandableFlyoutAdvancedSettings();
});
before(() => {
getEndpointIntegrationVersion().then((version) => {
createAgentPolicyTask(version).then((data) => {
indexedPolicy = data;
@ -49,7 +46,7 @@ describe(
});
});
after(() => {
afterEach(() => {
if (indexedPolicy) {
cy.task('deleteIndexedFleetEndpointPolicies', indexedPolicy);
}
@ -108,24 +105,21 @@ describe(
let indexedPolicy: IndexedFleetEndpointPolicyResponse;
let policy: PolicyData;
const twoMonthsAgo = moment.utc().subtract(2, 'months').format('YYYY-MM-DD');
const oneWeekAgo = moment.utc().subtract(1, 'weeks').format('YYYY-MM-DD');
beforeEach(() => {
login();
disableExpandableFlyoutAdvancedSettings();
});
before(() => {
getEndpointIntegrationVersion().then((version) => {
createAgentPolicyTask(version).then((data) => {
indexedPolicy = data;
policy = indexedPolicy.integrationPolicies[0];
setCustomProtectionUpdatesManifestVersion(policy.id, twoMonthsAgo);
setCustomProtectionUpdatesManifestVersion(policy.id, oneWeekAgo);
});
});
});
after(() => {
afterEach(() => {
if (indexedPolicy) {
cy.task('deleteIndexedFleetEndpointPolicies', indexedPolicy);
}
@ -133,7 +127,6 @@ describe(
it('should update manifest version to latest when enabling automatic updates', () => {
loadProtectionUpdatesUrl(policy.id);
cy.getByTestSubj('protection-updates-manifest-outdated');
cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled');
cy.getByTestSubj('protection-updates-manifest-switch').click();
@ -157,25 +150,22 @@ describe(
let indexedPolicy: IndexedFleetEndpointPolicyResponse;
let policy: PolicyData;
const twoMonthsAgo = moment.utc().subtract(2, 'months').format('YYYY-MM-DD');
const oneWeekAgo = moment.utc().subtract(1, 'weeks').format('YYYY-MM-DD');
beforeEach(() => {
login();
disableExpandableFlyoutAdvancedSettings();
});
before(() => {
getEndpointIntegrationVersion().then((version) => {
createAgentPolicyTask(version).then((data) => {
indexedPolicy = data;
policy = indexedPolicy.integrationPolicies[0];
setCustomProtectionUpdatesManifestVersion(policy.id, twoMonthsAgo);
setCustomProtectionUpdatesManifestVersion(policy.id, oneWeekAgo);
setCustomProtectionUpdatesNote(policy.id, testNote);
});
});
});
after(() => {
afterEach(() => {
if (indexedPolicy) {
cy.task('deleteIndexedFleetEndpointPolicies', indexedPolicy);
}
@ -200,9 +190,7 @@ describe(
cy.getByTestSubj('protectionUpdatesSuccessfulMessage');
cy.getByTestSubj('protection-updates-manifest-note').contains(updatedTestNote);
cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled');
});
it('should preserve note', () => {
loadProtectionUpdatesUrl(policy.id);
cy.getByTestSubj('protection-updates-manifest-note').contains(updatedTestNote);
});
@ -211,28 +199,22 @@ describe(
describe('Renders read only protection updates for user without write permissions', () => {
let indexedPolicy: IndexedFleetEndpointPolicyResponse;
let policy: PolicyData;
const twoMonthsAgo = moment.utc().subtract(2, 'months');
const oneWeekAgo = moment.utc().subtract(1, 'weeks');
beforeEach(() => {
login(ROLE.endpoint_security_policy_management_read);
disableExpandableFlyoutAdvancedSettings();
});
before(() => {
getEndpointIntegrationVersion().then((version) => {
createAgentPolicyTask(version).then((data) => {
indexedPolicy = data;
policy = indexedPolicy.integrationPolicies[0];
setCustomProtectionUpdatesManifestVersion(
policy.id,
twoMonthsAgo.format('YYYY-MM-DD')
);
setCustomProtectionUpdatesManifestVersion(policy.id, oneWeekAgo.format('YYYY-MM-DD'));
setCustomProtectionUpdatesNote(policy.id, testNote);
});
});
});
after(() => {
afterEach(() => {
if (indexedPolicy) {
cy.task('deleteIndexedFleetEndpointPolicies', indexedPolicy);
}
@ -246,7 +228,7 @@ describe(
cy.getByTestSubj('protection-updates-manifest-name-deployed-version-title');
cy.getByTestSubj('protection-updates-deployed-version').contains(
twoMonthsAgo.format('MMMM DD, YYYY')
oneWeekAgo.format('MMMM DD, YYYY')
);
cy.getByTestSubj('protection-updates-manifest-name-version-to-deploy-title');
cy.getByTestSubj('protection-updates-version-to-deploy-view-mode');

View file

@ -20,7 +20,8 @@ describe(
env: { ftrConfig: { enableExperimental: ['protectionUpdatesEnabled'] } },
},
() => {
describe('Renders policy list with outdated policies', () => {
// Today API wont let us create a policy with a manifest version before October 1st 2023
describe.skip('Renders policy list with outdated policies', () => {
const indexedPolicies: IndexedFleetEndpointPolicyResponse[] = [];
const monthAgo = moment.utc().subtract(1, 'months').format('YYYY-MM-DD');

View file

@ -38,6 +38,7 @@ import { useKibana, useToasts } from '../../../../../common/lib/kibana';
import { useUpdateEndpointPolicy } from '../../../../hooks/policy/use_update_endpoint_policy';
import type { PolicyData, MaybeImmutable } from '../../../../../../common/endpoint/types';
import { ProtectionUpdatesWarningPanel } from './components/protection_updates_warning_panel';
import { getControlledArtifactCutoffDate } from '../../../../../../common/endpoint/utils/controlled_artifact_rollout';
interface ProtectionUpdatesLayoutProps {
policy: MaybeImmutable<PolicyData>;
@ -92,7 +93,7 @@ export const ProtectionUpdatesLayout = React.memo<ProtectionUpdatesLayoutProps>(
const internalDateFormat = 'YYYY-MM-DD';
const displayDateFormat = 'MMMM DD, YYYY';
const formattedDate = moment.utc(deployedVersion, internalDateFormat).format(displayDateFormat);
const cutoffDate = moment.utc().subtract(18, 'months').add(1, 'day'); // Earliest selectable date
const cutoffDate = getControlledArtifactCutoffDate(); // Earliest selectable date
const viewModeSwitchLabel = automaticUpdatesEnabled
? AUTOMATIC_UPDATES_CHECKBOX_LABEL

View file

@ -453,7 +453,7 @@ describe('ingest_integration tests ', () => {
{
date: '2020-10-31',
message:
'Global manifest version is too far in the past. Use "latest" or a date within the last 18 months. UTC time.',
'Global manifest version is too far in the past. Please use either "latest" or a date within the last 18 months. The earliest valid date is October 1, 2023, in UTC time.',
},
{
date: '2100-10-01',

View file

@ -8,6 +8,7 @@
import moment from 'moment';
import type { NewPackagePolicyInput } from '@kbn/fleet-plugin/common';
import { getControlledArtifactCutoffDate } from '../../../common/endpoint/utils/controlled_artifact_rollout';
export const validateEndpointPackagePolicy = (inputs: NewPackagePolicyInput[]) => {
const input = inputs.find((i) => i.type === 'endpoint');
@ -22,10 +23,10 @@ export const validateEndpointPackagePolicy = (inputs: NewPackagePolicyInput[]) =
);
}
const maxAllowedDate = moment.utc().subtract(18, 'months');
const maxAllowedDate = getControlledArtifactCutoffDate();
if (parsedDate.isBefore(maxAllowedDate)) {
throw createManifestVersionError(
'Global manifest version is too far in the past. Use "latest" or a date within the last 18 months. UTC time.'
'Global manifest version is too far in the past. Please use either "latest" or a date within the last 18 months. The earliest valid date is October 1, 2023, in UTC time.'
);
}
if (parsedDate.isAfter(moment.utc())) {