Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
0135781
Initial Policy Settings component with Advanced Section
paul-tavares Jul 5, 2023
c2e728e
Fix issues with onChange and adjust types
paul-tavares Jul 5, 2023
bddf8e6
remove unused policy related service methods
paul-tavares Jul 6, 2023
766a4b6
change poc how form is displayed so that it keeps state
paul-tavares Jul 6, 2023
5394395
console.log updates to the policy (dev POC)
paul-tavares Jul 6, 2023
b0dbb7e
hook for retrieving endpoint policy details
paul-tavares Jul 6, 2023
1706257
Mostly working Malware card
paul-tavares Jul 6, 2023
5cf58db
Ensure user notification default messages are set
paul-tavares Jul 6, 2023
e92d874
malware card support for view only mode
paul-tavares Jul 6, 2023
38a7085
spacing adjustments to NotifyUserOption
paul-tavares Jul 6, 2023
0a0ffe1
Ransomeware protection card
paul-tavares Jul 6, 2023
7d1a4e3
Memory protection card
paul-tavares Jul 6, 2023
babbf21
Malicious Behaviour Protection card
paul-tavares Jul 6, 2023
d2d9d56
Attack Surface Reduction Card
paul-tavares Jul 6, 2023
f1c187e
Add form section title for settings
paul-tavares Jul 6, 2023
dfa39aa
Windows Event Collection card
paul-tavares Jul 7, 2023
045a93a
Mac event Collection card
paul-tavares Jul 7, 2023
ce132b5
Linux event Collection card
paul-tavares Jul 7, 2023
13c7e13
Register as Antivirus Card
paul-tavares Jul 7, 2023
1b4656d
Event collection adjustments for View only mode
paul-tavares Jul 7, 2023
3b9839c
minor adjustments
paul-tavares Jul 7, 2023
4ff75ce
Fix linux event card in view only mode
paul-tavares Jul 7, 2023
8884089
Fix event collection card types and `os` value map to policy os key
paul-tavares Jul 7, 2023
e63e826
new hook: `useUpdateEndpointPolicy()`
paul-tavares Jul 7, 2023
b2116c6
Initial Policy Settings (tab) layout
paul-tavares Jul 7, 2023
51cf751
policy update with success and failure toast messages
paul-tavares Jul 8, 2023
042d688
Show count of agents on confirm save dialog
paul-tavares Jul 8, 2023
db902cd
Fix type error
paul-tavares Jul 8, 2023
30cd82b
replace form displayed in Fleet and updates authz checks
paul-tavares Jul 8, 2023
7f515bf
delete old code
paul-tavares Jul 8, 2023
b06d855
remove unused code + add code to use data-test-subj
paul-tavares Jul 8, 2023
a91998e
re-organize the policy form cards
paul-tavares Jul 8, 2023
7ce18e8
move AgentSummary to component folder
paul-tavares Jul 8, 2023
6994b9d
Merge remote-tracking branch 'upstream/main' into task/olm-remove-red…
paul-tavares Jul 8, 2023
199790b
use `data-test-subj` in form cards
paul-tavares Jul 8, 2023
038bc2c
Fix jest tests
paul-tavares Jul 10, 2023
9dee22f
Merge remote-tracking branch 'upstream/main' into task/olm-remove-red…
paul-tavares Jul 10, 2023
bbd6894
Fix data-test-subj values
paul-tavares Jul 10, 2023
03366ad
policy details `visitPolicyDetailsPage()` now can take an optional po…
paul-tavares Jul 10, 2023
3c708b6
fix remainder of Cy policy details tests
paul-tavares Jul 10, 2023
d21550d
fix FTR test
paul-tavares Jul 10, 2023
4935c00
Add test subjects mock for re-use across test suites
paul-tavares Jul 11, 2023
77a8a09
Merge remote-tracking branch 'upstream/main' into task/olm-remove-red…
paul-tavares Jul 11, 2023
6c88df6
Re-apply changes from Candace's PR
paul-tavares Jul 11, 2023
5a02d50
update test subjects and fix failing tests
paul-tavares Jul 11, 2023
9b1e5a9
Fix cy policy details test
paul-tavares Jul 11, 2023
312ebc2
code review comments (ash)
paul-tavares Jul 12, 2023
bee92fb
Merge remote-tracking branch 'upstream/main' into task/olm-remove-red…
paul-tavares Jul 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ import type { MaybeImmutable, NewPolicyData, PolicyData } from '../../types';
*/
export const getPolicyDataForUpdate = (policy: MaybeImmutable<PolicyData>): NewPolicyData => {
// eslint-disable-next-line @typescript-eslint/naming-convention
const { id, revision, created_by, created_at, updated_by, updated_at, ...newPolicy } = policy;
// cast to `NewPolicyData` (mutable) since we cloned the entire object
const policyDataForUpdate = cloneDeep(newPolicy) as NewPolicyData;
const { id, revision, created_by, created_at, updated_by, updated_at, ...rest } =
policy as PolicyData;

const policyDataForUpdate: NewPolicyData = cloneDeep(rest);
const endpointPolicy = policyDataForUpdate.inputs[0].config.policy.value;

// trim custom malware notification string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import { getPolicySettingsFormTestSubjects } from '../../../pages/policy/view/policy_settings_form/mocks';
import { ProtectionModes } from '../../../../../common/endpoint/types';
import {
PackagePolicyBackupHelper,
Expand All @@ -30,7 +31,7 @@ describe('Policy Details', () => {

beforeEach(() => {
login();
visitPolicyDetailsPage();
visitPolicyDetailsPage(indexedHostsData.data.integrationPolicies[0].id);
});

afterEach(() => {
Expand All @@ -42,33 +43,43 @@ describe('Policy Details', () => {
});

describe('Malware Protection card', () => {
const malwareTestSubj = getPolicySettingsFormTestSubjects().malware;

it('user should be able to see related rules', () => {
cy.getByTestSubj('malwareProtectionsForm').contains('related detection rules').click();
cy.getByTestSubj(malwareTestSubj.card).contains('related detection rules').click();

cy.url().should('contain', 'app/security/rules/management');
});

it('changing protection level should enable or disable user notification', () => {
cy.getByTestSubj('malwareProtectionSwitch').click();
cy.getByTestSubj('malwareProtectionSwitch').should('have.attr', 'aria-checked', 'true');
cy.getByTestSubj(malwareTestSubj.enableDisableSwitch).click();
cy.getByTestSubj(malwareTestSubj.enableDisableSwitch).should(
'have.attr',
'aria-checked',
'true'
);

// Default: Prevent + Notify user enabled
cy.getByTestSubj('malwareProtectionMode_prevent').find('input').should('be.checked');
cy.getByTestSubj('malwareUserNotificationCheckbox').should('be.checked');
cy.getByTestSubj(malwareTestSubj.protectionPreventRadio).find('input').should('be.checked');
cy.getByTestSubj(malwareTestSubj.notifyUserCheckbox).should('be.checked');

// Changing to Detect -> Notify user disabled
cy.getByTestSubj('malwareProtectionMode_detect').find('label').click();
cy.getByTestSubj('malwareUserNotificationCheckbox').should('not.be.checked');
cy.getByTestSubj(malwareTestSubj.protectionDetectRadio).find('label').click();
cy.getByTestSubj(malwareTestSubj.notifyUserCheckbox).should('not.be.checked');

// Changing back to Prevent -> Notify user enabled
cy.getByTestSubj('malwareProtectionMode_prevent').find('label').click();
cy.getByTestSubj('malwareUserNotificationCheckbox').should('be.checked');
cy.getByTestSubj(malwareTestSubj.protectionPreventRadio).find('label').click();
cy.getByTestSubj(malwareTestSubj.notifyUserCheckbox).should('be.checked');
});

it('disabling protection should disable notification in yaml for every OS', () => {
// Enable malware protection and user notification
cy.getByTestSubj('malwareProtectionSwitch').click();
cy.getByTestSubj('malwareProtectionSwitch').should('have.attr', 'aria-checked', 'true');
cy.getByTestSubj(malwareTestSubj.enableDisableSwitch).click();
cy.getByTestSubj(malwareTestSubj.enableDisableSwitch).should(
'have.attr',
'aria-checked',
'true'
);
savePolicyForm();

yieldPolicyConfig().then((policyConfig) => {
Expand All @@ -78,8 +89,12 @@ describe('Policy Details', () => {
});

// disable malware protection
cy.getByTestSubj('malwareProtectionSwitch').click();
cy.getByTestSubj('malwareProtectionSwitch').should('have.attr', 'aria-checked', 'false');
cy.getByTestSubj(malwareTestSubj.enableDisableSwitch).click();
cy.getByTestSubj(malwareTestSubj.enableDisableSwitch).should(
'have.attr',
'aria-checked',
'false'
);
savePolicyForm();

yieldPolicyConfig().then((policyConfig) => {
Expand All @@ -96,11 +111,12 @@ describe('Policy Details', () => {
expect(policyConfig.windows.malware.mode).to.equal(ProtectionModes.off);
});

cy.getByTestSubj('malwareProtectionsForm').should('contain.text', 'Linux');
cy.getByTestSubj('malwareProtectionsForm').should('contain.text', 'Windows');
cy.getByTestSubj('malwareProtectionsForm').should('contain.text', 'Mac');
cy.getByTestSubj(malwareTestSubj.osValuesContainer).should(
'contain.text',
'Windows, Mac, Linux'
);

cy.getByTestSubj('malwareProtectionSwitch').click();
cy.getByTestSubj(malwareTestSubj.enableDisableSwitch).click();
savePolicyForm();

yieldPolicyConfig().then((policyConfig) => {
Expand All @@ -112,40 +128,50 @@ describe('Policy Details', () => {
});

describe('Ransomware Protection card', () => {
const ransomwareTestSubj = getPolicySettingsFormTestSubjects().ransomware;

it('user should be able to see related rules', () => {
cy.getByTestSubj('ransomwareProtectionsForm').contains('related detection rules').click();
cy.getByTestSubj(ransomwareTestSubj.card).contains('related detection rules').click();

cy.url().should('contain', 'app/security/rules/management');
});

it('changing protection level should enable or disable user notification', () => {
cy.getByTestSubj('ransomwareProtectionSwitch').click();
cy.getByTestSubj('ransomwareProtectionSwitch').should('have.attr', 'aria-checked', 'true');
cy.getByTestSubj(ransomwareTestSubj.enableDisableSwitch).click();
cy.getByTestSubj(ransomwareTestSubj.enableDisableSwitch).should(
'have.attr',
'aria-checked',
'true'
);

// Default: Prevent + Notify user enabled
cy.getByTestSubj('ransomwareProtectionMode_prevent').find('input').should('be.checked');
cy.getByTestSubj('ransomwareUserNotificationCheckbox').should('be.checked');
cy.getByTestSubj(ransomwareTestSubj.protectionPreventRadio)
.find('input')
.should('be.checked');
cy.getByTestSubj(ransomwareTestSubj.notifyUserCheckbox).should('be.checked');

// Changing to Detect -> Notify user disabled
cy.getByTestSubj('ransomwareProtectionMode_detect').find('label').click();
cy.getByTestSubj('ransomwareUserNotificationCheckbox').should('not.be.checked');
cy.getByTestSubj(ransomwareTestSubj.protectionDetectRadio).find('label').click();
cy.getByTestSubj(ransomwareTestSubj.notifyUserCheckbox).should('not.be.checked');

// Changing back to Prevent -> Notify user enabled
cy.getByTestSubj('ransomwareProtectionMode_prevent').find('label').click();
cy.getByTestSubj('ransomwareUserNotificationCheckbox').should('be.checked');
cy.getByTestSubj(ransomwareTestSubj.protectionPreventRadio).find('label').click();
cy.getByTestSubj(ransomwareTestSubj.notifyUserCheckbox).should('be.checked');
});
});

describe('Advanced settings', () => {
const testSubjects = getPolicySettingsFormTestSubjects().advancedSection;

it('should show empty text inputs except for some settings', () => {
const settingsWithDefaultValues = [
'mac.advanced.capture_env_vars',
'linux.advanced.capture_env_vars',
];

cy.getByTestSubj('advancedPolicyButton').click();
cy.getByTestSubj(testSubjects.showHideButton).click();

cy.getByTestSubj('advancedPolicyPanel')
cy.getByTestSubj(testSubjects.settingsContainer)
.children()
.each(($child) => {
const settingName = $child.find('label').text();
Expand All @@ -167,8 +193,8 @@ describe('Policy Details', () => {
});

// Set agent.connection_delay entry for every OS
cy.getByTestSubj('advancedPolicyButton').click();
cy.getByTestSubj('advancedPolicyPanel')
cy.getByTestSubj(testSubjects.showHideButton).click();
cy.getByTestSubj(testSubjects.settingsContainer)
.children()
.each(($child) => {
const settingName = $child.find('label').text();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ import type { PolicyConfig } from '../../../../common/endpoint/types';
import { request, loadPage } from '../tasks/common';
import { expectAndCloseSuccessToast } from '../tasks/toasts';

export const visitPolicyDetailsPage = () => {
loadPage(APP_POLICIES_PATH);

cy.getByTestSubj('policyNameCellLink').eq(0).click({ force: true });
export const visitPolicyDetailsPage = (policyId?: string) => {
if (policyId) {
loadPage(`${APP_POLICIES_PATH}/${policyId}`);
} else {
cy.visit(APP_POLICIES_PATH);
cy.getByTestSubj('policyNameCellLink').eq(0).click({ force: true });
}
cy.getByTestSubj('policyDetailsPage').should('exist');
cy.get('#settings').should('exist'); // waiting for Policy Settings tab
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* 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 { UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
import type { IHttpFetchError } from '@kbn/core-http-browser';
import { useQuery } from '@tanstack/react-query';
import { packagePolicyRouteService } from '@kbn/fleet-plugin/common';
import {
DefaultPolicyNotificationMessage,
DefaultPolicyRuleNotificationMessage,
} from '../../../../common/endpoint/models/policy_config';
import type { GetPolicyResponse } from '../../pages/policy/types';
import { useHttp } from '../../../common/lib/kibana';
import type { PolicyData, PolicyConfig } from '../../../../common/endpoint/types';
import type { ManifestSchema } from '../../../../common/endpoint/schema/manifest';

interface ApiDataResponse {
/** Data return from the Fleet API. Its the full integration policy (package policy) */
item: PolicyData;
/** Endpoint policy settings from the data retrieved from fleet */
settings: PolicyConfig;
/** Endpoint policy manifest info from the data retrieved from fleet */
artifactManifest: ManifestSchema;
}

type UseFetchEndpointPolicyResponse = UseQueryResult<ApiDataResponse, IHttpFetchError>;

/**
* Retrieve a single endpoint integration policy (details)
* @param policyId
* @param options
*/
export const useFetchEndpointPolicy = (
policyId: string,
options: UseQueryOptions<ApiDataResponse, IHttpFetchError> = {}
): UseFetchEndpointPolicyResponse => {
const http = useHttp();

return useQuery<ApiDataResponse, IHttpFetchError>({
queryKey: ['get-policy-details', policyId],
...options,
queryFn: async () => {
const apiResponse = await http.get<GetPolicyResponse>(
packagePolicyRouteService.getInfoPath(policyId)
);

applyDefaultsToPolicyIfNeeded(apiResponse.item);

return {
item: apiResponse.item,
settings: apiResponse.item.inputs[0].config.policy.value,
artifactManifest: apiResponse.item.inputs[0].config.artifact_manifest.value,
};
},
});
};

const applyDefaultsToPolicyIfNeeded = (policyItem: PolicyData): void => {
const settings = policyItem.inputs[0].config.policy.value;

// sets default user notification message if policy config message is empty
if (settings.windows.popup.malware.message === '') {
settings.windows.popup.malware.message = DefaultPolicyNotificationMessage;
settings.mac.popup.malware.message = DefaultPolicyNotificationMessage;
settings.linux.popup.malware.message = DefaultPolicyNotificationMessage;
}
if (settings.windows.popup.ransomware.message === '') {
settings.windows.popup.ransomware.message = DefaultPolicyNotificationMessage;
}
if (settings.windows.popup.memory_protection.message === '') {
settings.windows.popup.memory_protection.message = DefaultPolicyRuleNotificationMessage;
}
if (settings.mac.popup.memory_protection.message === '') {
settings.mac.popup.memory_protection.message = DefaultPolicyRuleNotificationMessage;
}
if (settings.linux.popup.memory_protection.message === '') {
settings.linux.popup.memory_protection.message = DefaultPolicyRuleNotificationMessage;
}
if (settings.windows.popup.behavior_protection.message === '') {
settings.windows.popup.behavior_protection.message = DefaultPolicyRuleNotificationMessage;
}
if (settings.mac.popup.behavior_protection.message === '') {
settings.mac.popup.behavior_protection.message = DefaultPolicyRuleNotificationMessage;
}
if (settings.linux.popup.behavior_protection.message === '') {
settings.linux.popup.behavior_protection.message = DefaultPolicyRuleNotificationMessage;
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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 { UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
import type { IHttpFetchError } from '@kbn/core-http-browser';
import type { GetAgentStatusResponse } from '@kbn/fleet-plugin/common';
import { useQuery } from '@tanstack/react-query';
import { agentRouteService } from '@kbn/fleet-plugin/common';
import { useHttp } from '../../../common/lib/kibana';

type EndpointPolicyAgentSummary = GetAgentStatusResponse['results'];

export const useFetchAgentByAgentPolicySummary = (
/**
* The Fleet Agent Policy ID (NOT the endpoint policy id)
*/
agentPolicyId: string,
options: UseQueryOptions<EndpointPolicyAgentSummary, IHttpFetchError> = {}
): UseQueryResult<EndpointPolicyAgentSummary, IHttpFetchError> => {
const http = useHttp();

return useQuery<EndpointPolicyAgentSummary, IHttpFetchError>({
queryKey: ['get-policy-agent-summary', agentPolicyId],
...options,
queryFn: async () => {
return (
await http.get<GetAgentStatusResponse>(agentRouteService.getStatusPath(), {
query: { policyId: agentPolicyId },
})
).results;
},
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* 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 { UseMutationOptions, UseMutationResult } from '@tanstack/react-query';
import type { IHttpFetchError } from '@kbn/core-http-browser';
import { useMutation } from '@tanstack/react-query';
import { packagePolicyRouteService } from '@kbn/fleet-plugin/common';
import { getPolicyDataForUpdate } from '../../../../common/endpoint/service/policy';
import { useHttp } from '../../../common/lib/kibana';
import type { PolicyData } from '../../../../common/endpoint/types';
import type { UpdatePolicyResponse } from '../../pages/policy/types';

interface UpdateParams {
policy: PolicyData;
}

type UseUpdateEndpointPolicyOptions = UseMutationOptions<
UpdatePolicyResponse,
IHttpFetchError,
UpdateParams
>;

type UseUpdateEndpointPolicyResult = UseMutationResult<
UpdatePolicyResponse,
IHttpFetchError,
UpdateParams
>;

export const useUpdateEndpointPolicy = (
options?: UseUpdateEndpointPolicyOptions
): UseUpdateEndpointPolicyResult => {
const http = useHttp();

return useMutation<UpdatePolicyResponse, IHttpFetchError, UpdateParams>(({ policy }) => {
const update = getPolicyDataForUpdate(policy);

return http.put(packagePolicyRouteService.getUpdatePath(policy.id), {
body: JSON.stringify(update),
});
}, options);
};
Loading