Skip to content

Commit

Permalink
[Security Solution] Enhance test coverage for Prebuilt Rules upgrade …
Browse files Browse the repository at this point in the history
…workflows (#204677)

Addresses: #202078

> NOTE: This PR depends on the test plans created in #203331. If any feedback leads to changes in those test plan, these PR will be updated.

## Summary

- Enhances tests on the Rule Upgrade Workflow from the Rule Updates table, as described in test plan created in: #203331

## Flaky test runner

https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/7611
  • Loading branch information
jpdjere authored Jan 2, 2025
1 parent c41cf9a commit 529f833
Show file tree
Hide file tree
Showing 3 changed files with 355 additions and 260 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const UpgradeWithConflictsModal = memo(function ConfirmUpgradeWithConflic
confirmButtonText={i18n.UPGRADE_CONFLICTS_MODAL_CONFIRM}
buttonColor="primary"
defaultFocusedButton="confirm"
data-test-subj="confirmUpgradeWithConflictsModal"
data-test-subj="upgradeConflictsModal"
>
<EuiText>{i18n.UPGRADE_CONFLICTS_MODAL_BODY}</EuiText>
</EuiConfirmModal>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,354 @@
/*
* 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 { getPrebuiltRuleMockOfType } from '@kbn/security-solution-plugin/server/lib/detection_engine/prebuilt_rules/mocks';
import {
RuleResponse,
RuleSignatureId,
} from '@kbn/security-solution-plugin/common/api/detection_engine';
import { createRuleAssetSavedObject } from '../../../../helpers/rules';
import {
MODIFIED_RULE_BADGE,
RULES_UPDATES_TABLE,
UPGRADE_ALL_RULES_BUTTON,
UPGRADE_SELECTED_RULES_BUTTON,
getUpgradeSingleRuleButtonByRuleId,
} from '../../../../screens/alerts_detection_rules';
import { selectRulesByName } from '../../../../tasks/alerts_detection_rules';
import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common';
import {
SAMPLE_PREBUILT_RULE,
createAndInstallMockedPrebuiltRules,
installPrebuiltRuleAssets,
} from '../../../../tasks/api_calls/prebuilt_rules';
import { patchRule } from '../../../../tasks/api_calls/rules';
import { resetRulesTableState } from '../../../../tasks/common';
import { login } from '../../../../tasks/login';
import {
assertRuleUpgradeConflictsModalShown,
assertRuleUpgradeSuccessToastShown,
assertRulesNotPresentInRuleUpdatesTable,
assertRulesPresentInRuleUpdatesTable,
assertUpgradeRequestIsComplete,
clickRuleUpdatesTab,
clickUpgradeRuleWithoutConflicts,
filterPrebuiltRulesUpdateTableByRuleCustomization,
} from '../../../../tasks/prebuilt_rules';
import { visitRulesManagementTable } from '../../../../tasks/rules_management';

describe(
'Detection rules, Prebuilt Rules Installation and Update workflow - With Rule Customization, Rule Updates Table',
{
tags: ['@ess', '@serverless', '@skipInServerlessMKI'],
env: {
ftrConfig: {
kbnServerArgs: [
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
'prebuiltRulesCustomizationEnabled',
])}`,
],
},
},
},

() => {
describe('Upgrade of prebuilt rules with conflicts', () => {
const RULE_1_ID = 'rule_1';
const RULE_2_ID = 'rule_2';
const OUTDATED_RULE_1 = createRuleAssetSavedObject({
name: 'Old rule 1',
rule_id: RULE_1_ID,
version: 1,
});
const UPDATED_RULE_1 = createRuleAssetSavedObject({
name: 'New rule 1',
rule_id: RULE_1_ID,
version: 2,
});
const OUTDATED_RULE_2 = createRuleAssetSavedObject({
name: 'Old rule 2',
rule_id: RULE_2_ID,
version: 1,
});
const UPDATED_RULE_2 = createRuleAssetSavedObject({
name: 'New rule 2',
rule_id: RULE_2_ID,
version: 2,
});
const patchedName = 'A new name that creates a conflict';

beforeEach(() => {
login();
resetRulesTableState();
deleteAlertsAndRules();
cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/upgrade/_perform').as(
'updatePrebuiltRules'
);

setUpRuleUpgrades({
currentRuleAssets: [OUTDATED_RULE_1, OUTDATED_RULE_2],
rulePatches: [
{ rule_id: RULE_1_ID, name: `Old rule 1 - ${patchedName}` },
{ rule_id: RULE_2_ID, name: `Old rule 2 - ${patchedName}` },
],
newRuleAssets: [UPDATED_RULE_1, UPDATED_RULE_2],
});

visitRulesManagementTable();
clickRuleUpdatesTab();
});

it('should disable individual upgrade buttons for all prebuilt rules with conflicts', () => {
// All buttons should be disabled because of conflicts
for (const rule of [OUTDATED_RULE_1, OUTDATED_RULE_2]) {
const { rule_id: ruleId } = rule['security-rule'];
expect(cy.get(getUpgradeSingleRuleButtonByRuleId(ruleId)).should('be.disabled'));
}
});

it('should disable `Update selected rules` button when all selected rules have conflicts', () => {
selectRulesByName(['Old rule 1', 'Old rule 2']);
cy.get(UPGRADE_SELECTED_RULES_BUTTON).should('be.disabled');
});

it('should disable `Update all rules` button when all rules have conflicts', () => {
cy.get(UPGRADE_ALL_RULES_BUTTON).should('be.disabled');
});
});

describe('Upgrade of prebuilt rules with and without conflicts', () => {
const RULE_1_ID = 'rule_1';
const RULE_2_ID = 'rule_2';
const RULE_3_ID = 'rule_3';
const OUTDATED_RULE_1 = createRuleAssetSavedObject({
name: 'Outdated rule 1',
rule_id: RULE_1_ID,
version: 1,
});
const UPDATED_RULE_1 = createRuleAssetSavedObject({
name: 'Updated rule 1',
rule_id: RULE_1_ID,
version: 2,
});
const OUTDATED_RULE_2 = createRuleAssetSavedObject({
name: 'Outdated rule 2',
rule_id: RULE_2_ID,
version: 1,
});
const UPDATED_RULE_2 = createRuleAssetSavedObject({
name: 'Updated rule 2',
rule_id: RULE_2_ID,
version: 2,
});
const OUTDATED_RULE_3 = createRuleAssetSavedObject({
name: 'Outdated rule 3',
rule_id: RULE_3_ID,
version: 1,
});
const UPDATED_RULE_3 = createRuleAssetSavedObject({
name: 'Updated rule 3',
rule_id: RULE_3_ID,
version: 2,
});
const patchedName = 'Conflicting rule name';

beforeEach(() => {
login();
resetRulesTableState();
deleteAlertsAndRules();
cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/upgrade/_perform').as(
'updatePrebuiltRules'
);

setUpRuleUpgrades({
currentRuleAssets: [OUTDATED_RULE_1, OUTDATED_RULE_2, OUTDATED_RULE_3],
rulePatches: [{ rule_id: RULE_1_ID, name: patchedName }],
newRuleAssets: [UPDATED_RULE_1, UPDATED_RULE_2, UPDATED_RULE_3],
});

visitRulesManagementTable();
clickRuleUpdatesTab();
});

it('should filter by customized prebuilt rules', () => {
// Filter table to show modified rules only
filterPrebuiltRulesUpdateTableByRuleCustomization('Modified');
cy.get(MODIFIED_RULE_BADGE).should('exist');

// Verify only rules with customized rule sources are displayed
cy.get(RULES_UPDATES_TABLE).contains(patchedName);
assertRulesNotPresentInRuleUpdatesTable([OUTDATED_RULE_2]);
});

it('should filter by non-customized prebuilt rules', () => {
// Filter table to show unmodified rules only
filterPrebuiltRulesUpdateTableByRuleCustomization('Unmodified');
cy.get(MODIFIED_RULE_BADGE).should('not.exist');

// Verify only rules with non-customized rule sources are displayed
assertRulesPresentInRuleUpdatesTable([OUTDATED_RULE_2]);
cy.get(patchedName).should('not.exist');
});

it('should allow upgrading rules without conflicts one by one', () => {
cy.get(
getUpgradeSingleRuleButtonByRuleId(OUTDATED_RULE_2['security-rule'].rule_id)
).click();
assertUpgradeRequestIsComplete([OUTDATED_RULE_2]);
assertRuleUpgradeSuccessToastShown([OUTDATED_RULE_2]);
assertRulesNotPresentInRuleUpdatesTable([OUTDATED_RULE_2]);

// Verify conflicting rule and another un-upgraded rule remain
cy.get(RULES_UPDATES_TABLE).contains(patchedName);
assertRulesPresentInRuleUpdatesTable([OUTDATED_RULE_3]);
});

it('should disable the upgrade button for conflicting rules while allowing upgrades of no-conflict rules', () => {
// Verify the conflicting rule's upgrade button is disabled
expect(
cy
.get(getUpgradeSingleRuleButtonByRuleId(OUTDATED_RULE_1['security-rule'].rule_id))
.should('be.disabled')
);

// Verify non-conflicting rules' upgrade buttons are enabled
expect(
cy
.get(getUpgradeSingleRuleButtonByRuleId(OUTDATED_RULE_2['security-rule'].rule_id))
.should('not.be.disabled')
);
expect(
cy
.get(getUpgradeSingleRuleButtonByRuleId(OUTDATED_RULE_3['security-rule'].rule_id))
.should('not.be.disabled')
);
});

it('should warn about rules with conflicts not being upgrading when upgrading a set of selected rules', () => {
selectRulesByName([
patchedName, // Rule with conflict
OUTDATED_RULE_2['security-rule'].name, // Rule without conflict
OUTDATED_RULE_3['security-rule'].name, // Rule without conflict
]);
cy.get(UPGRADE_SELECTED_RULES_BUTTON).click();
assertRuleUpgradeConflictsModalShown();
clickUpgradeRuleWithoutConflicts();

// Assert only rules without conflicts are upgraded
assertUpgradeRequestIsComplete([OUTDATED_RULE_2, OUTDATED_RULE_3]);
assertRuleUpgradeSuccessToastShown([OUTDATED_RULE_2, OUTDATED_RULE_3]);
assertRulesNotPresentInRuleUpdatesTable([OUTDATED_RULE_2, OUTDATED_RULE_3]);

// Verify conflicting rule remains in the table
cy.get(RULES_UPDATES_TABLE).contains(patchedName);
});

it('should warn about rules with conflicts not being upgrading when upgrading all rules', () => {
cy.get(UPGRADE_ALL_RULES_BUTTON).click();
assertRuleUpgradeConflictsModalShown();
clickUpgradeRuleWithoutConflicts();

// Assert only rules without conflicts are upgraded
assertUpgradeRequestIsComplete([OUTDATED_RULE_2, OUTDATED_RULE_3]);
assertRuleUpgradeSuccessToastShown([OUTDATED_RULE_2, OUTDATED_RULE_3]);
assertRulesNotPresentInRuleUpdatesTable([OUTDATED_RULE_2, OUTDATED_RULE_3]);

// Verify conflicting rule remains in the table
cy.get(RULES_UPDATES_TABLE).contains(patchedName);
});
});

describe('Upgrade of prebuilt rules with rule type changes', () => {
const RULE_1_ID = 'rule_1';
const RULE_2_ID = 'rule_2';
const OUTDATED_QUERY_RULE_1 = createRuleAssetSavedObject({
...getPrebuiltRuleMockOfType('query'),
name: 'Outdated query rule 1',
rule_id: RULE_1_ID,
version: 1,
});
const UPDATED_ESQL_RULE_1 = createRuleAssetSavedObject({
...getPrebuiltRuleMockOfType('esql'),
name: 'Updated rule 1',
rule_id: RULE_1_ID,
version: 2,
});
const OUTDATED_QUERY_RULE_2 = createRuleAssetSavedObject({
...getPrebuiltRuleMockOfType('query'),
name: 'Outdated query rule 2',
rule_id: RULE_2_ID,
version: 1,
});
const UPDATED_ESQL_RULE_2 = createRuleAssetSavedObject({
...getPrebuiltRuleMockOfType('esql'),
name: 'Updated rule 2',
rule_id: RULE_2_ID,
version: 2,
});

beforeEach(() => {
login();
resetRulesTableState();
deleteAlertsAndRules();
cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/upgrade/_perform').as(
'updatePrebuiltRules'
);

setUpRuleUpgrades({
currentRuleAssets: [OUTDATED_QUERY_RULE_1, OUTDATED_QUERY_RULE_2],
rulePatches: [],
newRuleAssets: [UPDATED_ESQL_RULE_1, UPDATED_ESQL_RULE_2],
});

visitRulesManagementTable();
clickRuleUpdatesTab();
});

it('should disable individual upgrade button for all rules', () => {
// All buttons should be disabled because rule type changes are considered conflicts
for (const rule of [OUTDATED_QUERY_RULE_1, OUTDATED_QUERY_RULE_2]) {
const { rule_id: ruleId } = rule['security-rule'];
expect(cy.get(getUpgradeSingleRuleButtonByRuleId(ruleId)).should('be.disabled'));
}
});

it('should disable `Update selected rules` button for all selected rules', () => {
selectRulesByName([
OUTDATED_QUERY_RULE_1['security-rule'].name,
OUTDATED_QUERY_RULE_2['security-rule'].name,
]);
cy.get(UPGRADE_SELECTED_RULES_BUTTON).should('be.disabled');
});

it('should disable `Update all rules` button', () => {
cy.get(UPGRADE_ALL_RULES_BUTTON).should('be.disabled');
});
});
}
);

interface SetUpRulesParams {
currentRuleAssets: Array<typeof SAMPLE_PREBUILT_RULE>;
newRuleAssets: Array<typeof SAMPLE_PREBUILT_RULE>;
rulePatches: Array<{ rule_id: RuleSignatureId } & Partial<RuleResponse>>;
}

function setUpRuleUpgrades({
currentRuleAssets,
newRuleAssets,
rulePatches,
}: SetUpRulesParams): void {
/* Create a new rule and install it */
createAndInstallMockedPrebuiltRules(currentRuleAssets);

for (const rule of rulePatches) {
const { rule_id: ruleId, ...update } = rule;
patchRule(ruleId, update);
}
/* Create a second version of the rule, making it available for update */
installPrebuiltRuleAssets(newRuleAssets);
}
Loading

0 comments on commit 529f833

Please sign in to comment.