Skip to content

Commit d185b71

Browse files
authored
[8.8] [Security Solution][Testing] Improve policy related endpoint regression test coverage (#157412) (#159482)
# Backport This will backport the following commits from `main` to `8.8`: - [[Security Solution][Testing] Improve policy related endpoint regression test coverage (#157412)](#157412) <!--- Backport version: 8.9.7 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Gergő Ábrahám","email":"gergo.abraham@elastic.co"},"sourceCommit":{"committedDate":"2023-05-30T09:21:14Z","message":"[Security Solution][Testing] Improve policy related endpoint regression test coverage (#157412)\n\n## Summary\r\n\r\n- Adds automated test coverage for Policy Detail page related manual\r\ntests. See linked issue for test cases.\r\n- fixes e2e alerts tests\r\n\r\n~Note: 3 more e2e test cases are coming soon.~\r\nUpdate: ready to review, the 3 e2e test cases will be added in a\r\nseparate PR.","sha":"771c8790d7f05bfde308506c4eb4174540057d11","branchLabelMapping":{"^v8.9.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","backport:skip","Team:Defend Workflows","v8.9.0"],"number":157412,"url":"https://github.com/elastic/kibana/pull/157412","mergeCommit":{"message":"[Security Solution][Testing] Improve policy related endpoint regression test coverage (#157412)\n\n## Summary\r\n\r\n- Adds automated test coverage for Policy Detail page related manual\r\ntests. See linked issue for test cases.\r\n- fixes e2e alerts tests\r\n\r\n~Note: 3 more e2e test cases are coming soon.~\r\nUpdate: ready to review, the 3 e2e test cases will be added in a\r\nseparate PR.","sha":"771c8790d7f05bfde308506c4eb4174540057d11"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v8.9.0","labelRegex":"^v8.9.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/157412","number":157412,"mergeCommit":{"message":"[Security Solution][Testing] Improve policy related endpoint regression test coverage (#157412)\n\n## Summary\r\n\r\n- Adds automated test coverage for Policy Detail page related manual\r\ntests. See linked issue for test cases.\r\n- fixes e2e alerts tests\r\n\r\n~Note: 3 more e2e test cases are coming soon.~\r\nUpdate: ready to review, the 3 e2e test cases will be added in a\r\nseparate PR.","sha":"771c8790d7f05bfde308506c4eb4174540057d11"}}]}] BACKPORT-->
1 parent 639d1a3 commit d185b71

17 files changed

Lines changed: 469 additions & 59 deletions

File tree

x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint/isolate.cy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import type { Agent } from '@kbn/fleet-plugin/common';
99
import { APP_CASES_PATH, APP_ENDPOINTS_PATH } from '../../../../../common/constants';
10-
import { closeAllToasts } from '../../tasks/close_all_toasts';
10+
import { closeAllToasts } from '../../tasks/toasts';
1111
import {
1212
checkEndpointListForOnlyIsolatedHosts,
1313
checkEndpointListForOnlyUnIsolatedHosts,

x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/artifact_tabs_in_policy_details.cy.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import { getEndpointSecurityPolicyManager } from '../../../../../scripts/endpoint/common/roles_users/endpoint_security_policy_manager';
99
import { getArtifactsListTestsData } from '../../fixtures/artifacts_page';
10+
import { visitPolicyDetailsPage } from '../../screens/policy_details';
1011
import {
1112
createPerPolicyArtifact,
1213
createArtifactList,
@@ -62,13 +63,6 @@ const visitArtifactTab = (tabId: string) => {
6263
cy.get(`#${tabId}`).click();
6364
};
6465

65-
const visitPolicyDetailsPage = () => {
66-
cy.visit('/app/security/administration/policy');
67-
cy.getByTestSubj('policyNameCellLink').eq(0).click({ force: true });
68-
cy.getByTestSubj('policyDetailsPage').should('exist');
69-
cy.get('#settings').should('exist'); // waiting for Policy Settings tab
70-
};
71-
7266
describe('Artifact tabs in Policy Details page', () => {
7367
before(() => {
7468
login();

x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/endpoint_role_rbac.cy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* 2.0.
66
*/
77

8-
import { closeAllToasts } from '../../tasks/close_all_toasts';
8+
import { closeAllToasts } from '../../tasks/toasts';
99
import { login } from '../../tasks/login';
1010

1111
describe('When defining a kibana role for Endpoint security access', () => {

x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/isolate.cy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
waitForReleaseOption,
2020
} from '../../tasks/isolate';
2121
import type { ActionDetails } from '../../../../../common/endpoint/types';
22-
import { closeAllToasts } from '../../tasks/close_all_toasts';
22+
import { closeAllToasts } from '../../tasks/toasts';
2323
import type { ReturnTypeFromChainable } from '../../types';
2424
import { addAlertsToCase } from '../../tasks/add_alerts_to_case';
2525
import { APP_ALERTS_PATH, APP_CASES_PATH, APP_PATH } from '../../../../../common/constants';
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import { ProtectionModes } from '../../../../../common/endpoint/types';
9+
import {
10+
PackagePolicyBackupHelper,
11+
savePolicyForm,
12+
visitPolicyDetailsPage,
13+
yieldPolicyConfig,
14+
} from '../../screens/policy_details';
15+
import type { CyIndexEndpointHosts } from '../../tasks/index_endpoint_hosts';
16+
import { indexEndpointHosts } from '../../tasks/index_endpoint_hosts';
17+
import { login } from '../../tasks/login';
18+
19+
describe('Policy Details', () => {
20+
const packagePolicyBackupHelper = new PackagePolicyBackupHelper();
21+
let indexedHostsData: CyIndexEndpointHosts;
22+
23+
before(() => {
24+
login();
25+
indexEndpointHosts().then((results) => {
26+
indexedHostsData = results;
27+
});
28+
packagePolicyBackupHelper.backup();
29+
});
30+
31+
beforeEach(() => {
32+
login();
33+
visitPolicyDetailsPage();
34+
});
35+
36+
afterEach(() => {
37+
packagePolicyBackupHelper.restore();
38+
});
39+
40+
after(() => {
41+
indexedHostsData.cleanup();
42+
});
43+
44+
describe('Malware Protection card', () => {
45+
it('user should be able to see related rules', () => {
46+
cy.getByTestSubj('malwareProtectionsForm').contains('related detection rules').click();
47+
48+
cy.url().should('contain', 'app/security/rules/management');
49+
});
50+
51+
it('changing protection level should enable or disable user notification', () => {
52+
cy.getByTestSubj('malwareProtectionSwitch').click();
53+
cy.getByTestSubj('malwareProtectionSwitch').should('have.attr', 'aria-checked', 'true');
54+
55+
// Default: Prevent + Notify user enabled
56+
cy.getByTestSubj('malwareProtectionMode_prevent').find('input').should('be.checked');
57+
cy.getByTestSubj('malwareUserNotificationCheckbox').should('be.checked');
58+
59+
// Changing to Detect -> Notify user disabled
60+
cy.getByTestSubj('malwareProtectionMode_detect').find('label').click();
61+
cy.getByTestSubj('malwareUserNotificationCheckbox').should('not.be.checked');
62+
63+
// Changing back to Prevent -> Notify user enabled
64+
cy.getByTestSubj('malwareProtectionMode_prevent').find('label').click();
65+
cy.getByTestSubj('malwareUserNotificationCheckbox').should('be.checked');
66+
});
67+
68+
it('disabling protection should disable notification in yaml for every OS', () => {
69+
// Enable malware protection and user notification
70+
cy.getByTestSubj('malwareProtectionSwitch').click();
71+
cy.getByTestSubj('malwareProtectionSwitch').should('have.attr', 'aria-checked', 'true');
72+
savePolicyForm();
73+
74+
yieldPolicyConfig().then((policyConfig) => {
75+
expect(policyConfig.mac.popup.malware.enabled).to.equal(true);
76+
expect(policyConfig.windows.popup.malware.enabled).to.equal(true);
77+
expect(policyConfig.linux.popup.malware.enabled).to.equal(true);
78+
});
79+
80+
// disable malware protection
81+
cy.getByTestSubj('malwareProtectionSwitch').click();
82+
cy.getByTestSubj('malwareProtectionSwitch').should('have.attr', 'aria-checked', 'false');
83+
savePolicyForm();
84+
85+
yieldPolicyConfig().then((policyConfig) => {
86+
expect(policyConfig.mac.popup.malware.enabled).to.equal(false);
87+
expect(policyConfig.windows.popup.malware.enabled).to.equal(false);
88+
expect(policyConfig.linux.popup.malware.enabled).to.equal(false);
89+
});
90+
});
91+
92+
it('user should be able to enable Malware protection for every OS in yaml', () => {
93+
yieldPolicyConfig().then((policyConfig) => {
94+
expect(policyConfig.linux.malware.mode).to.equal(ProtectionModes.off);
95+
expect(policyConfig.mac.malware.mode).to.equal(ProtectionModes.off);
96+
expect(policyConfig.windows.malware.mode).to.equal(ProtectionModes.off);
97+
});
98+
99+
cy.getByTestSubj('malwareProtectionsForm').should('contain.text', 'Linux');
100+
cy.getByTestSubj('malwareProtectionsForm').should('contain.text', 'Windows');
101+
cy.getByTestSubj('malwareProtectionsForm').should('contain.text', 'Mac');
102+
103+
cy.getByTestSubj('malwareProtectionSwitch').click();
104+
savePolicyForm();
105+
106+
yieldPolicyConfig().then((policyConfig) => {
107+
expect(policyConfig.linux.malware.mode).to.equal(ProtectionModes.prevent);
108+
expect(policyConfig.mac.malware.mode).to.equal(ProtectionModes.prevent);
109+
expect(policyConfig.windows.malware.mode).to.equal(ProtectionModes.prevent);
110+
});
111+
});
112+
});
113+
114+
describe('Ransomware Protection card', () => {
115+
it('user should be able to see related rules', () => {
116+
cy.getByTestSubj('ransomwareProtectionsForm').contains('related detection rules').click();
117+
118+
cy.url().should('contain', 'app/security/rules/management');
119+
});
120+
121+
it('changing protection level should enable or disable user notification', () => {
122+
cy.getByTestSubj('ransomwareProtectionSwitch').click();
123+
cy.getByTestSubj('ransomwareProtectionSwitch').should('have.attr', 'aria-checked', 'true');
124+
125+
// Default: Prevent + Notify user enabled
126+
cy.getByTestSubj('ransomwareProtectionMode_prevent').find('input').should('be.checked');
127+
cy.getByTestSubj('ransomwareUserNotificationCheckbox').should('be.checked');
128+
129+
// Changing to Detect -> Notify user disabled
130+
cy.getByTestSubj('ransomwareProtectionMode_detect').find('label').click();
131+
cy.getByTestSubj('ransomwareUserNotificationCheckbox').should('not.be.checked');
132+
133+
// Changing back to Prevent -> Notify user enabled
134+
cy.getByTestSubj('ransomwareProtectionMode_prevent').find('label').click();
135+
cy.getByTestSubj('ransomwareUserNotificationCheckbox').should('be.checked');
136+
});
137+
});
138+
139+
describe('Advanced settings', () => {
140+
it('should show empty text inputs except for some settings', () => {
141+
const settingsWithDefaultValues = [
142+
'mac.advanced.capture_env_vars',
143+
'linux.advanced.capture_env_vars',
144+
];
145+
146+
cy.getByTestSubj('advancedPolicyButton').click();
147+
148+
cy.getByTestSubj('advancedPolicyPanel')
149+
.children()
150+
.each(($child) => {
151+
const settingName = $child.find('label').text();
152+
153+
if (settingsWithDefaultValues.includes(settingName)) {
154+
cy.wrap($child).find('input').should('not.have.value', '');
155+
} else {
156+
cy.wrap($child).find('input').should('have.value', '');
157+
}
158+
});
159+
});
160+
161+
it('should add advanced settings to policy yaml only when they are set', () => {
162+
// Initially config should only contain the two default entries
163+
yieldPolicyConfig().then((policyConfig) => {
164+
expect(policyConfig.linux.advanced).to.have.keys(['capture_env_vars']);
165+
expect(policyConfig.mac.advanced).to.have.keys(['capture_env_vars']);
166+
expect(policyConfig.windows.advanced).to.equal(undefined);
167+
});
168+
169+
// Set agent.connection_delay entry for every OS
170+
cy.getByTestSubj('advancedPolicyButton').click();
171+
cy.getByTestSubj('advancedPolicyPanel')
172+
.children()
173+
.each(($child) => {
174+
const settingName = $child.find('label').text();
175+
176+
if (settingName.includes('.agent.connection_delay')) {
177+
cy.wrap($child).find('input').type('66');
178+
}
179+
});
180+
savePolicyForm();
181+
182+
// Validate yaml
183+
yieldPolicyConfig().then((policyConfig) => {
184+
expect(policyConfig.linux.advanced).to.have.keys(['capture_env_vars', 'agent']);
185+
expect(policyConfig.linux.advanced).to.have.deep.property('agent', {
186+
connection_delay: '66',
187+
});
188+
expect(policyConfig.mac.advanced).to.have.keys(['capture_env_vars', 'agent']);
189+
expect(policyConfig.mac.advanced).to.have.deep.property('agent', {
190+
connection_delay: '66',
191+
});
192+
expect(policyConfig.windows.advanced).to.have.keys(['agent']);
193+
expect(policyConfig.windows.advanced).to.have.deep.property('agent', {
194+
connection_delay: '66',
195+
});
196+
});
197+
});
198+
});
199+
});

x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/responder.cy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* 2.0.
66
*/
77

8-
import { closeAllToasts } from '../../tasks/close_all_toasts';
8+
import { closeAllToasts } from '../../tasks/toasts';
99
import type { ReturnTypeFromChainable } from '../../types';
1010
import { addAlertsToCase } from '../../tasks/add_alerts_to_case';
1111
import { APP_CASES_PATH } from '../../../../../common/constants';
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import type {
9+
GetOnePackagePolicyResponse,
10+
GetPackagePoliciesResponse,
11+
PackagePolicy,
12+
UpdatePackagePolicy,
13+
UpdatePackagePolicyResponse,
14+
} from '@kbn/fleet-plugin/common';
15+
import { packagePolicyRouteService } from '@kbn/fleet-plugin/common';
16+
import { APP_POLICIES_PATH } from '../../../../common/constants';
17+
import type { PolicyConfig } from '../../../../common/endpoint/types';
18+
import { request } from '../tasks/common';
19+
import { expectAndCloseSuccessToast } from '../tasks/toasts';
20+
21+
export const visitPolicyDetailsPage = () => {
22+
cy.visit(APP_POLICIES_PATH);
23+
24+
cy.getByTestSubj('policyNameCellLink').eq(0).click({ force: true });
25+
cy.getByTestSubj('policyDetailsPage').should('exist');
26+
cy.get('#settings').should('exist'); // waiting for Policy Settings tab
27+
};
28+
29+
export const savePolicyForm = () => {
30+
cy.getByTestSubj('policyDetailsSaveButton').click();
31+
cy.getByTestSubj('confirmModalConfirmButton').click();
32+
expectAndCloseSuccessToast();
33+
};
34+
35+
export const yieldPolicyConfig = (): Cypress.Chainable<PolicyConfig> => {
36+
return cy
37+
.url()
38+
.then((url) => {
39+
const policyId = url.match(/security\/administration\/policy\/([a-z0-9-]+)/)?.[1];
40+
expect(policyId).to.not.equal(undefined);
41+
42+
return policyId;
43+
})
44+
.then((policyId: string) => {
45+
return request<GetOnePackagePolicyResponse>({
46+
url: `/api/fleet/package_policies/${policyId}`,
47+
});
48+
})
49+
.then((res) => {
50+
const firstPackagePolicyConfig = res.body.item.inputs[0].config;
51+
expect(firstPackagePolicyConfig).to.not.equal(undefined);
52+
53+
const policyConfig: PolicyConfig = firstPackagePolicyConfig?.policy.value;
54+
55+
return policyConfig;
56+
});
57+
};
58+
59+
export class PackagePolicyBackupHelper {
60+
originalPackagePolicy!: PackagePolicy;
61+
62+
backup() {
63+
request<GetPackagePoliciesResponse>({
64+
url: packagePolicyRouteService.getListPath(),
65+
qs: {
66+
kuery: 'ingest-package-policies.package.name: endpoint',
67+
},
68+
}).then((res) => {
69+
this.originalPackagePolicy = res.body.items[0];
70+
});
71+
}
72+
73+
restore() {
74+
const body: UpdatePackagePolicy = {
75+
name: this.originalPackagePolicy.name,
76+
namespace: this.originalPackagePolicy.namespace,
77+
enabled: this.originalPackagePolicy.enabled,
78+
inputs: this.originalPackagePolicy.inputs,
79+
policy_id: this.originalPackagePolicy.policy_id,
80+
};
81+
82+
request<UpdatePackagePolicyResponse>({
83+
method: 'PUT',
84+
url: packagePolicyRouteService.getUpdatePath(this.originalPackagePolicy.id),
85+
body,
86+
});
87+
}
88+
}

x-pack/plugins/security_solution/public/management/cypress/tasks/response_console.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
*/
77

88
import type { ConsoleResponseActionCommands } from '../../../../common/endpoint/service/response_actions/constants';
9-
import { closeAllToasts } from './close_all_toasts';
9+
import { closeAllToasts } from './toasts';
1010
import { APP_ENDPOINTS_PATH } from '../../../../common/constants';
1111
import Chainable = Cypress.Chainable;
1212

x-pack/plugins/security_solution/public/management/cypress/tasks/close_all_toasts.ts renamed to x-pack/plugins/security_solution/public/management/cypress/tasks/toasts.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,9 @@ export const closeAllToasts = (): Cypress.Chainable<JQuery<HTMLBodyElement>> =>
4040
});
4141
});
4242
};
43+
44+
export const expectAndCloseSuccessToast = () => {
45+
cy.contains('Success');
46+
cy.getByTestSubj('toastCloseButton').click();
47+
cy.contains('Success').should('not.exist');
48+
};

x-pack/plugins/security_solution/public/management/pages/policy/view/components/antivirus_registration_form/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export const AntivirusRegistrationForm = memo(() => {
5858
<ConfigForm
5959
type={TRANSLATIONS.title}
6060
supportedOss={[OperatingSystem.WINDOWS]}
61+
dataTestSubj="antivirusRegistrationForm"
6162
osRestriction={i18n.translate(
6263
'xpack.securitySolution.endpoint.policy.details.av.windowsServerNotSupported',
6364
{

0 commit comments

Comments
 (0)