diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/export_rule.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/export_rule.cy.ts
index 9a360d211f3fa..fecf13b21ddeb 100644
--- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/export_rule.cy.ts
+++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/export_rule.cy.ts
@@ -5,21 +5,25 @@
* 2.0.
*/
-import { expectedExportedRule, getNewRule } from '../../objects/rule';
+import path from 'path';
+import { expectedExportedRule, getNewRule } from '../../objects/rule';
import {
TOASTER_BODY,
MODAL_CONFIRMATION_BODY,
MODAL_CONFIRMATION_BTN,
+ TOASTER,
} from '../../screens/alerts_detection_rules';
-
import {
- exportFirstRule,
loadPrebuiltDetectionRulesFromHeaderBtn,
filterByElasticRules,
selectNumberOfRules,
bulkExportRules,
selectAllRules,
+ waitForRuleExecution,
+ exportRule,
+ importRules,
+ expectManagementTableRules,
} from '../../tasks/alerts_detection_rules';
import { createExceptionList, deleteExceptionList } from '../../tasks/api_calls/exceptions';
import { getExceptionList } from '../../objects/exception';
@@ -30,9 +34,12 @@ import { login, visitWithoutDateRange } from '../../tasks/login';
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation';
import { getAvailablePrebuiltRulesCount } from '../../tasks/api_calls/prebuilt_rules';
+const EXPORTED_RULES_FILENAME = 'rules_export.ndjson';
const exceptionList = getExceptionList();
describe('Export rules', () => {
+ const downloadsFolder = Cypress.config('downloadsFolder');
+
before(() => {
cleanKibana();
login();
@@ -45,17 +52,34 @@ describe('Export rules', () => {
// Rules get exported via _bulk_action endpoint
cy.intercept('POST', '/api/detection_engine/rules/_bulk_action').as('bulk_action');
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
- createRule(getNewRule()).as('ruleResponse');
+ createRule({ ...getNewRule(), name: 'Rule to export' }).as('ruleResponse');
});
- it('Exports a custom rule', function () {
- exportFirstRule();
+ it('exports a custom rule', function () {
+ exportRule('Rule to export');
cy.wait('@bulk_action').then(({ response }) => {
cy.wrap(response?.body).should('eql', expectedExportedRule(this.ruleResponse));
cy.get(TOASTER_BODY).should('have.text', 'Successfully exported 1 of 1 rule.');
});
});
+ it('creates an importable file from executed rule', () => {
+ // Rule needs to be enabled to make sure it has been executed so rule's SO contains runtime fields like `execution_summary`
+ createRule({ ...getNewRule(), name: 'Enabled rule to export', enabled: true });
+ waitForRuleExecution('Enabled rule to export');
+
+ exportRule('Enabled rule to export');
+
+ cy.get(TOASTER).should('have.text', 'Rules exported');
+ cy.get(TOASTER_BODY).should('have.text', 'Successfully exported 1 of 1 rule.');
+
+ deleteAlertsAndRules();
+ importRules(path.join(downloadsFolder, EXPORTED_RULES_FILENAME));
+
+ cy.get(TOASTER).should('have.text', 'Successfully imported 1 rule');
+ expectManagementTableRules(['Enabled rule to export']);
+ });
+
it('shows a modal saying that no rules can be exported if all the selected rules are prebuilt', function () {
const expectedElasticRulesCount = 7;
diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/import_rules.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/import_rules.cy.ts
index 647edd716bc49..5bb0f44b0e5ec 100644
--- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/import_rules.cy.ts
+++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/import_rules.cy.ts
@@ -5,10 +5,9 @@
* 2.0.
*/
-import { RULES_MANAGEMENT_TABLE, TOASTER } from '../../screens/alerts_detection_rules';
+import { TOASTER } from '../../screens/alerts_detection_rules';
import {
- expectNumberOfRules,
- expectToContainRule,
+ expectManagementTableRules,
importRules,
importRulesWithOverwriteAll,
} from '../../tasks/alerts_detection_rules';
@@ -16,6 +15,7 @@ import { cleanKibana, deleteAlertsAndRules, reload } from '../../tasks/common';
import { login, visitWithoutDateRange } from '../../tasks/login';
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation';
+const RULES_TO_IMPORT_FILENAME = 'cypress/fixtures/7_16_rules.ndjson';
describe('Import rules', () => {
before(() => {
@@ -29,10 +29,7 @@ describe('Import rules', () => {
});
it('Imports a custom rule with exceptions', function () {
- const expectedNumberOfRules = 1;
- const expectedImportedRuleName = 'Test Custom Rule';
-
- importRules('7_16_rules.ndjson');
+ importRules(RULES_TO_IMPORT_FILENAME);
cy.wait('@import').then(({ response }) => {
cy.wrap(response?.statusCode).should('eql', 200);
@@ -41,20 +38,19 @@ describe('Import rules', () => {
'Successfully imported 1 ruleSuccessfully imported 1 exception.'
);
- expectNumberOfRules(RULES_MANAGEMENT_TABLE, expectedNumberOfRules);
- expectToContainRule(RULES_MANAGEMENT_TABLE, expectedImportedRuleName);
+ expectManagementTableRules(['Test Custom Rule']);
});
});
it('Shows error toaster when trying to import rule and exception list that already exist', function () {
- importRules('7_16_rules.ndjson');
+ importRules(RULES_TO_IMPORT_FILENAME);
cy.wait('@import').then(({ response }) => {
cy.wrap(response?.statusCode).should('eql', 200);
});
reload();
- importRules('7_16_rules.ndjson');
+ importRules(RULES_TO_IMPORT_FILENAME);
cy.wait('@import').then(({ response }) => {
cy.wrap(response?.statusCode).should('eql', 200);
@@ -63,14 +59,14 @@ describe('Import rules', () => {
});
it('Does not show error toaster when trying to import rule and exception list that already exist when overwrite is true', function () {
- importRules('7_16_rules.ndjson');
+ importRules(RULES_TO_IMPORT_FILENAME);
cy.wait('@import').then(({ response }) => {
cy.wrap(response?.statusCode).should('eql', 200);
});
reload();
- importRulesWithOverwriteAll('7_16_rules.ndjson');
+ importRulesWithOverwriteAll(RULES_TO_IMPORT_FILENAME);
cy.wait('@import').then(({ response }) => {
cy.wrap(response?.statusCode).should('eql', 200);
diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/persistent_rules_table_state.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/persistent_rules_table_state.cy.ts
index 3d9653ae383ee..7685c2a08c7e4 100644
--- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/persistent_rules_table_state.cy.ts
+++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/persistent_rules_table_state.cy.ts
@@ -17,8 +17,6 @@ import {
} from '../../urls/navigation';
import { getNewRule } from '../../objects/rule';
import {
- expectNumberOfRules,
- expectToContainRule,
filterByCustomRules,
filterBySearchTerm,
filterByTags,
@@ -35,8 +33,8 @@ import {
filterByDisabledRules,
expectFilterByPrebuiltRules,
expectFilterByEnabledRules,
+ expectManagementTableRules,
} from '../../tasks/alerts_detection_rules';
-import { RULES_MANAGEMENT_TABLE } from '../../screens/alerts_detection_rules';
import { createRule } from '../../tasks/api_calls/rules';
import {
expectRowsPerPage,
@@ -108,14 +106,6 @@ function expectDefaultRulesTableState(): void {
expectTablePage(1);
}
-function expectManagementTableRules(ruleNames: string[]): void {
- expectNumberOfRules(RULES_MANAGEMENT_TABLE, ruleNames.length);
-
- for (const ruleName of ruleNames) {
- expectToContainRule(RULES_MANAGEMENT_TABLE, ruleName);
- }
-}
-
describe('Persistent rules table state', () => {
before(() => {
cleanKibana();
diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts
index 47bf62a73c3f8..2af59e2ae67d7 100644
--- a/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts
+++ b/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts
@@ -71,6 +71,8 @@ export const RULE_CHECKBOX = '.euiTableRow .euiCheckbox__input';
export const RULE_NAME = '[data-test-subj="ruleName"]';
+export const RULE_LAST_RUN = '[data-test-subj="ruleLastRun"]';
+
export const RULE_SWITCH = '[data-test-subj="ruleSwitch"]';
export const RULE_SWITCH_LOADER = '[data-test-subj="ruleSwitchLoader"]';
@@ -143,6 +145,8 @@ export const SELECTED_RULES_NUMBER_LABEL = '[data-test-subj="selectedRules"]';
export const REFRESH_SETTINGS_POPOVER = '[data-test-subj="refreshSettings-popover"]';
+export const REFRESH_RULES_TABLE_BUTTON = '[data-test-subj="refreshRulesAction-linkIcon"]';
+
export const REFRESH_SETTINGS_SWITCH = '[data-test-subj="refreshSettingsSwitch"]';
export const REFRESH_SETTINGS_SELECTION_NOTE = '[data-test-subj="refreshSettingsSelectionNote"]';
diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts
index 12d63a198cb06..f17fbac688f9d 100644
--- a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts
+++ b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts
@@ -60,6 +60,8 @@ import {
RULES_MONITORING_TAB,
ENABLED_RULES_BTN,
DISABLED_RULES_BTN,
+ REFRESH_RULES_TABLE_BUTTON,
+ RULE_LAST_RUN,
} from '../screens/alerts_detection_rules';
import type { RULES_MONITORING_TABLE } from '../screens/alerts_detection_rules';
import { EUI_CHECKBOX } from '../screens/common/controls';
@@ -162,8 +164,10 @@ export const disableSelectedRules = () => {
cy.get(DISABLE_RULE_BULK_BTN).click();
};
-export const exportFirstRule = () => {
- cy.get(COLLAPSED_ACTION_BTN).first().click({ force: true });
+export const exportRule = (name: string) => {
+ cy.log(`Export rule "${name}"`);
+
+ cy.contains(RULE_NAME, name).parents(RULES_ROW).find(COLLAPSED_ACTION_BTN).click();
cy.get(EXPORT_ACTION_BTN).click();
cy.get(EXPORT_ACTION_BTN).should('not.exist');
};
@@ -183,6 +187,19 @@ export const filterByTags = (tags: string[]) => {
}
};
+export const waitForRuleExecution = (name: string) => {
+ cy.log(`Wait for rule "${name}" to be executed`);
+ cy.waitUntil(() => {
+ cy.get(REFRESH_RULES_TABLE_BUTTON).click();
+
+ return cy
+ .contains(RULE_NAME, name)
+ .parents(RULES_ROW)
+ .find(RULE_LAST_RUN)
+ .then(($el) => $el.text().endsWith('ago'));
+ });
+};
+
export const filterByElasticRules = () => {
cy.get(ELASTIC_RULES_BTN).click();
waitForRulesTableToBeRefreshed();
@@ -358,7 +375,7 @@ export const checkAutoRefresh = (ms: number, condition: string) => {
export const importRules = (rulesFile: string) => {
cy.get(RULE_IMPORT_MODAL).click();
cy.get(INPUT_FILE).should('exist');
- cy.get(INPUT_FILE).trigger('click', { force: true }).attachFile(rulesFile).trigger('change');
+ cy.get(INPUT_FILE).trigger('click', { force: true }).selectFile(rulesFile).trigger('change');
cy.get(RULE_IMPORT_MODAL_BUTTON).last().click({ force: true });
cy.get(INPUT_FILE).should('not.exist');
};
@@ -455,6 +472,14 @@ const selectOverwriteRulesImport = () => {
.should('be.checked');
};
+export const expectManagementTableRules = (ruleNames: string[]): void => {
+ expectNumberOfRules(RULES_MANAGEMENT_TABLE, ruleNames.length);
+
+ for (const ruleName of ruleNames) {
+ expectToContainRule(RULES_MANAGEMENT_TABLE, ruleName);
+ }
+};
+
const selectOverwriteExceptionsRulesImport = () => {
cy.get(RULE_IMPORT_OVERWRITE_EXCEPTIONS_CHECKBOX)
.pipe(($el) => $el.trigger('click'))
@@ -468,7 +493,7 @@ const selectOverwriteConnectorsRulesImport = () => {
export const importRulesWithOverwriteAll = (rulesFile: string) => {
cy.get(RULE_IMPORT_MODAL).click();
cy.get(INPUT_FILE).should('exist');
- cy.get(INPUT_FILE).trigger('click', { force: true }).attachFile(rulesFile).trigger('change');
+ cy.get(INPUT_FILE).trigger('click', { force: true }).selectFile(rulesFile).trigger('change');
selectOverwriteRulesImport();
selectOverwriteExceptionsRulesImport();
selectOverwriteConnectorsRulesImport();
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_columns.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_columns.tsx
index fdc9f5f3a5b43..1296e9728d2e6 100644
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_columns.tsx
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_columns.tsx
@@ -278,15 +278,19 @@ export const useRulesColumns = ({
field: 'execution_summary.last_execution.date',
name: i18n.COLUMN_LAST_COMPLETE_RUN,
render: (value: RuleExecutionSummary['last_execution']['date'] | undefined) => {
- return value == null ? (
- getEmptyTagValue()
- ) : (
-
+ return (
+
+ {value == null ? (
+ getEmptyTagValue()
+ ) : (
+
+ )}
+
);
},
sortable: true,
@@ -438,15 +442,19 @@ export const useMonitoringColumns = ({
field: 'execution_summary.last_execution.date',
name: i18n.COLUMN_LAST_COMPLETE_RUN,
render: (value: RuleExecutionSummary['last_execution']['date'] | undefined) => {
- return value == null ? (
- getEmptyTagValue()
- ) : (
-
+ return (
+
+ {value == null ? (
+ getEmptyTagValue()
+ ) : (
+
+ )}
+
);
},
sortable: true,
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.ts
index 88f3e9019d493..5da8576c04a58 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.ts
@@ -12,7 +12,7 @@ import type { ExceptionListClient } from '@kbn/lists-plugin/server';
import type { RulesClient, RuleExecutorServices } from '@kbn/alerting-plugin/server';
import { getNonPackagedRules } from '../search/get_existing_prepackaged_rules';
import { getExportDetailsNdjson } from './get_export_details_ndjson';
-import { transformAlertsToRules } from '../../utils/utils';
+import { transformAlertsToRules, transformRuleToExportableFormat } from '../../utils/utils';
import { getRuleExceptionsForExport } from './get_export_rule_exceptions';
import { getRuleActionConnectorsForExport } from './get_export_rule_action_connectors';
@@ -42,6 +42,7 @@ export const getExportAll = async (
logger,
});
const rules = transformAlertsToRules(ruleAlertTypes, legacyActions);
+ const exportRules = rules.map((r) => transformRuleToExportableFormat(r));
// Gather exceptions
const exceptions = rules.flatMap((rule) => rule.exceptions_list ?? []);
@@ -55,7 +56,7 @@ export const getExportAll = async (
request
);
- const rulesNdjson = transformDataToNdjson(rules);
+ const rulesNdjson = transformDataToNdjson(exportRules);
const exportDetails = getExportDetailsNdjson(rules, [], exceptionDetails, actionConnectorDetails);
return { rulesNdjson, exportDetails, exceptionLists, actionConnectors };
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.ts
index 90938420e64b5..5221c4f26761b 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.ts
@@ -17,6 +17,7 @@ import { getExportDetailsNdjson } from './get_export_details_ndjson';
import { isAlertType } from '../../../rule_schema';
import { findRules } from '../search/find_rules';
+import { transformRuleToExportableFormat } from '../../utils/utils';
import { getRuleExceptionsForExport } from './get_export_rule_exceptions';
import { getRuleActionConnectorsForExport } from './get_export_rule_action_connectors';
@@ -133,14 +134,11 @@ export const getRulesFromObjects = async (
isAlertType(matchingRule) &&
matchingRule.params.immutable !== true
) {
- const rule = internalRuleToAPIResponse(matchingRule, legacyActions[matchingRule.id]);
-
- // Fields containing runtime information shouldn't be exported. It causes import failures.
- delete rule.execution_summary;
-
return {
statusCode: 200,
- rule,
+ rule: transformRuleToExportableFormat(
+ internalRuleToAPIResponse(matchingRule, legacyActions[matchingRule.id])
+ ),
};
} else {
return {
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.ts
index d024de62a95bf..3f65a49795d75 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.ts
@@ -98,6 +98,23 @@ export const transformAlertsToRules = (
return rules.map((rule) => internalRuleToAPIResponse(rule, legacyRuleActions[rule.id]));
};
+/**
+ * Transforms a rule object to exportable format. Exportable format shouldn't contain runtime fields like
+ * `execution_summary`
+ */
+export const transformRuleToExportableFormat = (
+ rule: RuleResponse
+): Omit => {
+ const exportedRule = {
+ ...rule,
+ };
+
+ // Fields containing runtime information shouldn't be exported. It causes import failures.
+ delete exportedRule.execution_summary;
+
+ return exportedRule;
+};
+
export const transformFindAlerts = (
ruleFindResults: FindResult,
legacyRuleActions: Record
diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts
index 945388de7a54e..0946852e6a117 100644
--- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts
+++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts
@@ -5,9 +5,10 @@
* 2.0.
*/
-import expect from '@kbn/expect';
+import expect from 'expect';
import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants';
+import { RuleExecutionStatus } from '@kbn/security-solution-plugin/common/detection_engine/rule_monitoring';
import { FtrProviderContext } from '../../common/ftr_provider_context';
import {
binaryToString,
@@ -19,6 +20,7 @@ import {
getSimpleRuleOutput,
getWebHookAction,
removeServerGeneratedProperties,
+ waitForRuleSuccessOrStatus,
} from '../../utils';
// eslint-disable-next-line import/no-default-export
@@ -49,8 +51,61 @@ export default ({ getService }: FtrProviderContext): void => {
.expect('Content-Disposition', 'attachment; filename="export.ndjson"');
});
- it('should export a single rule with a rule_id', async () => {
- await createRule(supertest, log, getSimpleRule());
+ it('should validate exported rule schema when its exported by its rule_id', async () => {
+ const ruleId = 'rule-1';
+
+ const rule = await createRule(supertest, log, getSimpleRule(ruleId, true));
+
+ await waitForRuleSuccessOrStatus(
+ supertest,
+ log,
+ rule.id,
+ RuleExecutionStatus['partial failure']
+ );
+ // to properly execute the test on rule's data with runtime fields some delay is needed as
+ // ES Search API may return outdated data
+ // it causes a reliable delay so exported rule's SO contains runtime fields returned via ES Search API
+ // and will be removed after addressing this issue
+ await new Promise((r) => setTimeout(r, 1000));
+
+ const { body } = await supertest
+ .post(`${DETECTION_ENGINE_RULES_URL}/_export`)
+ .set('kbn-xsrf', 'true')
+ .send({
+ objects: [{ rule_id: 'rule-1' }],
+ })
+ .expect(200)
+ .parse(binaryToString);
+
+ const exportedRule = JSON.parse(body.toString().split(/\n/)[0]);
+
+ expectToMatchRuleSchema(exportedRule);
+ });
+
+ it('should validate all exported rules schema', async () => {
+ const ruleId1 = 'rule-1';
+ const ruleId2 = 'rule-2';
+
+ const rule1 = await createRule(supertest, log, getSimpleRule(ruleId1, true));
+ const rule2 = await createRule(supertest, log, getSimpleRule(ruleId2, true));
+
+ await waitForRuleSuccessOrStatus(
+ supertest,
+ log,
+ rule1.id,
+ RuleExecutionStatus['partial failure']
+ );
+ await waitForRuleSuccessOrStatus(
+ supertest,
+ log,
+ rule2.id,
+ RuleExecutionStatus['partial failure']
+ );
+ // to properly execute the test on rule's data with runtime fields some delay is needed as
+ // ES Search API may return outdated data
+ // it causes a reliable delay so exported rule's SO contains runtime fields returned via ES Search API
+ // and will be removed after addressing this issue
+ await new Promise((r) => setTimeout(r, 1000));
const { body } = await supertest
.post(`${DETECTION_ENGINE_RULES_URL}/_export`)
@@ -59,10 +114,11 @@ export default ({ getService }: FtrProviderContext): void => {
.expect(200)
.parse(binaryToString);
- const bodySplitAndParsed = JSON.parse(body.toString().split(/\n/)[0]);
- const bodyToTest = removeServerGeneratedProperties(bodySplitAndParsed);
+ const exportedRule1 = JSON.parse(body.toString().split(/\n/)[1]);
+ const exportedRule2 = JSON.parse(body.toString().split(/\n/)[0]);
- expect(bodyToTest).to.eql(getSimpleRuleOutput());
+ expectToMatchRuleSchema(exportedRule1);
+ expectToMatchRuleSchema(exportedRule2);
});
it('should export a exported count with a single rule_id', async () => {
@@ -77,7 +133,7 @@ export default ({ getService }: FtrProviderContext): void => {
const bodySplitAndParsed = JSON.parse(body.toString().split(/\n/)[1]);
- expect(bodySplitAndParsed).to.eql({
+ expect(bodySplitAndParsed).toEqual({
exported_exception_list_count: 0,
exported_exception_list_item_count: 0,
exported_count: 1,
@@ -112,7 +168,7 @@ export default ({ getService }: FtrProviderContext): void => {
const firstRule = removeServerGeneratedProperties(firstRuleParsed);
const secondRule = removeServerGeneratedProperties(secondRuleParsed);
- expect([firstRule, secondRule]).to.eql([
+ expect([firstRule, secondRule]).toEqual([
getSimpleRuleOutput('rule-2'),
getSimpleRuleOutput('rule-1'),
]);
@@ -171,7 +227,7 @@ export default ({ getService }: FtrProviderContext): void => {
],
throttle: 'rule',
};
- expect(firstRule).to.eql(outputRule1);
+ expect(firstRule).toEqual(outputRule1);
});
it('should export actions attached to 2 rules', async () => {
@@ -224,8 +280,8 @@ export default ({ getService }: FtrProviderContext): void => {
actions: [{ ...action, uuid: secondRule.actions[0].uuid }],
throttle: 'rule',
};
- expect(firstRule).to.eql(outputRule1);
- expect(secondRule).to.eql(outputRule2);
+ expect(firstRule).toEqual(outputRule1);
+ expect(secondRule).toEqual(outputRule2);
});
/**
@@ -291,7 +347,7 @@ export default ({ getService }: FtrProviderContext): void => {
const firstRuleParsed = JSON.parse(body.toString().split(/\n/)[0]);
const firstRule = removeServerGeneratedProperties(firstRuleParsed);
- expect(firstRule).to.eql(outputRule1);
+ expect(firstRule).toEqual(outputRule1);
});
it('should be able to export 2 legacy actions on 1 rule', async () => {
@@ -377,7 +433,7 @@ export default ({ getService }: FtrProviderContext): void => {
const firstRuleParsed = JSON.parse(body.toString().split(/\n/)[0]);
const firstRule = removeServerGeneratedProperties(firstRuleParsed);
- expect(firstRule).to.eql(outputRule1);
+ expect(firstRule).toEqual(outputRule1);
});
it('should be able to export 2 legacy actions on 2 rules', async () => {
@@ -521,10 +577,50 @@ export default ({ getService }: FtrProviderContext): void => {
const firstRule = removeServerGeneratedProperties(firstRuleParsed);
const secondRule = removeServerGeneratedProperties(secondRuleParsed);
- expect(firstRule).to.eql(outputRule2);
- expect(secondRule).to.eql(outputRule1);
+ expect(firstRule).toEqual(outputRule2);
+ expect(secondRule).toEqual(outputRule1);
});
});
});
});
};
+
+function expectToMatchRuleSchema(obj: unknown): void {
+ expect(obj).toEqual({
+ id: expect.any(String),
+ rule_id: expect.any(String),
+ enabled: expect.any(Boolean),
+ immutable: false,
+ updated_at: expect.any(String),
+ updated_by: expect.any(String),
+ created_at: expect.any(String),
+ created_by: expect.any(String),
+ name: expect.any(String),
+ tags: expect.arrayContaining([]),
+ interval: expect.any(String),
+ description: expect.any(String),
+ risk_score: expect.any(Number),
+ severity: expect.any(String),
+ output_index: expect.any(String),
+ author: expect.arrayContaining([]),
+ false_positives: expect.arrayContaining([]),
+ from: expect.any(String),
+ max_signals: expect.any(Number),
+ risk_score_mapping: expect.arrayContaining([]),
+ severity_mapping: expect.arrayContaining([]),
+ threat: expect.arrayContaining([]),
+ to: expect.any(String),
+ references: expect.arrayContaining([]),
+ version: expect.any(Number),
+ exceptions_list: expect.arrayContaining([]),
+ related_integrations: expect.arrayContaining([]),
+ required_fields: expect.arrayContaining([]),
+ setup: expect.any(String),
+ type: expect.any(String),
+ language: expect.any(String),
+ index: expect.arrayContaining([]),
+ query: expect.any(String),
+ throttle: expect.any(String),
+ actions: expect.arrayContaining([]),
+ });
+}