Skip to content

Commit

Permalink
[Security Solution][Detections] Improves indicator match Cypress tests (
Browse files Browse the repository at this point in the history
#94913)

* updates the data used in the test

* adds matches test

* adds enrichment test

* improves speed and adds missing files

* fixes type check issue

* adds 'data-test-subj' for the json view tab

* refactor

* fixes typecheck issue

* updates tests with latest master changes

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
MadameSheema and kibanamachine authored Mar 25, 2021
1 parent 02fce98 commit 6a57148
Show file tree
Hide file tree
Showing 17 changed files with 1,075 additions and 3,615 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
ALERT_RULE_VERSION,
NUMBER_OF_ALERTS,
} from '../../screens/alerts';
import { JSON_LINES } from '../../screens/alerts_details';
import {
CUSTOM_RULES_BTN,
RISK_SCORE,
Expand Down Expand Up @@ -50,14 +51,17 @@ import {
SCHEDULE_DETAILS,
SEVERITY_DETAILS,
TAGS_DETAILS,
TIMELINE_FIELD,
TIMELINE_TEMPLATE_DETAILS,
} from '../../screens/rule_details';

import {
expandFirstAlert,
goToManageAlertsDetectionRules,
waitForAlertsIndexToBeCreated,
waitForAlertsPanelToBeLoaded,
} from '../../tasks/alerts';
import { openJsonView, scrollJsonViewToBottom } from '../../tasks/alerts_details';
import {
changeRowsPerPageTo300,
duplicateFirstRule,
Expand Down Expand Up @@ -98,7 +102,7 @@ import {
import { waitForKibana } from '../../tasks/edit_rule';
import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver';
import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login';
import { goBackToAllRulesTable } from '../../tasks/rule_details';
import { addsFieldsToTimeline, goBackToAllRulesTable } from '../../tasks/rule_details';

import { DETECTIONS_URL, RULE_CREATION } from '../../urls/navigation';

Expand All @@ -114,11 +118,11 @@ describe('indicator match', () => {
before(() => {
cleanKibana();
esArchiverLoad('threat_indicator');
esArchiverLoad('threat_data');
esArchiverLoad('suspicious_source_event');
});
after(() => {
esArchiverUnload('threat_indicator');
esArchiverUnload('threat_data');
esArchiverUnload('suspicious_source_event');
});

describe('Creating new indicator match rules', () => {
Expand Down Expand Up @@ -216,7 +220,7 @@ describe('indicator match', () => {

it('Does NOT show invalidation text when there is a valid "index field" and a valid "indicator index field"', () => {
fillIndicatorMatchRow({
indexField: newThreatIndicatorRule.indicatorMapping,
indexField: newThreatIndicatorRule.indicatorMappingField,
indicatorIndexField: newThreatIndicatorRule.indicatorIndexField,
});
getDefineContinueButton().click();
Expand All @@ -235,7 +239,7 @@ describe('indicator match', () => {

it('Shows invalidation text when there is a valid "index field" and an invalid "indicator index field"', () => {
fillIndicatorMatchRow({
indexField: newThreatIndicatorRule.indicatorMapping,
indexField: newThreatIndicatorRule.indicatorMappingField,
indicatorIndexField: 'non-existent-value',
validColumns: 'indexField',
});
Expand All @@ -245,7 +249,7 @@ describe('indicator match', () => {

it('Deletes the first row when you have two rows. Both rows valid rows of "index fields" and valid "indicator index fields". The second row should become the first row', () => {
fillIndicatorMatchRow({
indexField: newThreatIndicatorRule.indicatorMapping,
indexField: newThreatIndicatorRule.indicatorMappingField,
indicatorIndexField: newThreatIndicatorRule.indicatorIndexField,
});
getIndicatorAndButton().click();
Expand All @@ -267,14 +271,14 @@ describe('indicator match', () => {

it('Deletes the first row when you have two rows. Both rows have valid "index fields" and invalid "indicator index fields". The second row should become the first row', () => {
fillIndicatorMatchRow({
indexField: newThreatIndicatorRule.indicatorMapping,
indexField: newThreatIndicatorRule.indicatorMappingField,
indicatorIndexField: 'non-existent-value',
validColumns: 'indexField',
});
getIndicatorAndButton().click();
fillIndicatorMatchRow({
rowNumber: 2,
indexField: newThreatIndicatorRule.indicatorMapping,
indexField: newThreatIndicatorRule.indicatorMappingField,
indicatorIndexField: 'second-non-existent-value',
validColumns: 'indexField',
});
Expand Down Expand Up @@ -305,7 +309,7 @@ describe('indicator match', () => {

it('Deletes the first row of data but not the UI elements and the text defaults back to the placeholder of Search', () => {
fillIndicatorMatchRow({
indexField: newThreatIndicatorRule.indicatorMapping,
indexField: newThreatIndicatorRule.indicatorMappingField,
indicatorIndexField: newThreatIndicatorRule.indicatorIndexField,
});
getIndicatorDeleteButton().click();
Expand All @@ -317,7 +321,7 @@ describe('indicator match', () => {

it('Deletes the second row when you have three rows. The first row is valid data, the second row is invalid data, and the third row is valid data. Third row should shift up correctly', () => {
fillIndicatorMatchRow({
indexField: newThreatIndicatorRule.indicatorMapping,
indexField: newThreatIndicatorRule.indicatorMappingField,
indicatorIndexField: newThreatIndicatorRule.indicatorIndexField,
});
getIndicatorAndButton().click();
Expand All @@ -330,16 +334,22 @@ describe('indicator match', () => {
getIndicatorAndButton().click();
fillIndicatorMatchRow({
rowNumber: 3,
indexField: newThreatIndicatorRule.indicatorMapping,
indexField: newThreatIndicatorRule.indicatorMappingField,
indicatorIndexField: newThreatIndicatorRule.indicatorIndexField,
});
getIndicatorDeleteButton(2).click();
getIndicatorIndexComboField(1).should('text', newThreatIndicatorRule.indicatorMapping);
getIndicatorIndexComboField(1).should(
'text',
newThreatIndicatorRule.indicatorMappingField
);
getIndicatorMappingComboField(1).should(
'text',
newThreatIndicatorRule.indicatorIndexField
);
getIndicatorIndexComboField(2).should('text', newThreatIndicatorRule.indicatorMapping);
getIndicatorIndexComboField(2).should(
'text',
newThreatIndicatorRule.indicatorMappingField
);
getIndicatorMappingComboField(2).should(
'text',
newThreatIndicatorRule.indicatorIndexField
Expand All @@ -357,11 +367,14 @@ describe('indicator match', () => {
getIndicatorOrButton().click();
fillIndicatorMatchRow({
rowNumber: 2,
indexField: newThreatIndicatorRule.indicatorMapping,
indexField: newThreatIndicatorRule.indicatorMappingField,
indicatorIndexField: newThreatIndicatorRule.indicatorIndexField,
});
getIndicatorDeleteButton().click();
getIndicatorIndexComboField().should('text', newThreatIndicatorRule.indicatorMapping);
getIndicatorIndexComboField().should(
'text',
newThreatIndicatorRule.indicatorMappingField
);
getIndicatorMappingComboField().should(
'text',
newThreatIndicatorRule.indicatorIndexField
Expand Down Expand Up @@ -441,7 +454,7 @@ describe('indicator match', () => {
);
getDetails(INDICATOR_MAPPING).should(
'have.text',
`${newThreatIndicatorRule.indicatorMapping} MATCHES ${newThreatIndicatorRule.indicatorIndexField}`
`${newThreatIndicatorRule.indicatorMappingField} MATCHES ${newThreatIndicatorRule.indicatorIndexField}`
);
getDetails(INDICATOR_INDEX_QUERY).should('have.text', '*:*');
});
Expand Down Expand Up @@ -471,6 +484,74 @@ describe('indicator match', () => {
});
});

describe('Enrichment', () => {
const fieldSearch = 'threat.indicator.matched';
const fields = [
'threat.indicator.matched.atomic',
'threat.indicator.matched.type',
'threat.indicator.matched.field',
];
const expectedFieldsText = [
newThreatIndicatorRule.atomic,
newThreatIndicatorRule.type,
newThreatIndicatorRule.indicatorMappingField,
];

const expectedEnrichment = [
{ line: 4, text: ' "threat": {' },
{
line: 3,
text:
' "indicator": "{\\"first_seen\\":\\"2021-03-10T08:02:14.000Z\\",\\"file\\":{\\"size\\":80280,\\"pe\\":{},\\"type\\":\\"elf\\",\\"hash\\":{\\"sha256\\":\\"a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3\\",\\"tlsh\\":\\"6D7312E017B517CC1371A8353BED205E9128223972AE35302E97528DF957703BAB2DBE\\",\\"ssdeep\\":\\"1536:87vbq1lGAXSEYQjbChaAU2yU23M51DjZgSQAvcYkFtZTjzBht5:8D+CAXFYQChaAUk5ljnQssL\\",\\"md5\\":\\"9b6c3518a91d23ed77504b5416bfb5b3\\"}},\\"type\\":\\"file\\",\\"matched\\":{\\"atomic\\":\\"a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3\\",\\"field\\":\\"myhash.mysha256\\",\\"id\\":\\"84cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f\\",\\"index\\":\\"filebeat-7.12.0-2021.03.10-000001\\",\\"type\\":\\"file\\"}}"',
},
{ line: 2, text: ' }' },
];

before(() => {
cleanKibana();
esArchiverLoad('threat_indicator');
esArchiverLoad('suspicious_source_event');
loginAndWaitForPageWithoutDateRange(DETECTIONS_URL);
goToManageAlertsDetectionRules();
createCustomIndicatorRule(newThreatIndicatorRule);
reload();
});

after(() => {
esArchiverUnload('threat_indicator');
esArchiverUnload('suspicious_source_event');
});

beforeEach(() => {
loginAndWaitForPageWithoutDateRange(DETECTIONS_URL);
goToManageAlertsDetectionRules();
goToRuleDetails();
});

it('Displays matches on the timeline', () => {
addsFieldsToTimeline(fieldSearch, fields);

fields.forEach((field, index) => {
cy.get(TIMELINE_FIELD(field)).should('have.text', expectedFieldsText[index]);
});
});

it('Displays enrichment on the JSON view', () => {
expandFirstAlert();
openJsonView();
scrollJsonViewToBottom();

cy.get(JSON_LINES).then((elements) => {
const length = elements.length;
expectedEnrichment.forEach((enrichment) => {
cy.wrap(elements)
.eq(length - enrichment.line)
.should('have.text', enrichment.text);
});
});
});
});

describe('Duplicates the indicator rule', () => {
beforeEach(() => {
cleanKibana();
Expand Down
14 changes: 9 additions & 5 deletions x-pack/plugins/security_solution/cypress/objects/rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,10 @@ export interface OverrideRule extends CustomRule {

export interface ThreatIndicatorRule extends CustomRule {
indicatorIndexPattern: string[];
indicatorMapping: string;
indicatorMappingField: string;
indicatorIndexField: string;
type?: string;
atomic?: string;
}

export interface MachineLearningRule {
Expand Down Expand Up @@ -299,7 +301,7 @@ export const eqlSequenceRule: CustomRule = {
export const newThreatIndicatorRule: ThreatIndicatorRule = {
name: 'Threat Indicator Rule Test',
description: 'The threat indicator rule description.',
index: ['threat-data-*'],
index: ['suspicious-*'],
severity: 'Critical',
riskScore: '20',
tags: ['test', 'threat'],
Expand All @@ -309,9 +311,11 @@ export const newThreatIndicatorRule: ThreatIndicatorRule = {
note: '# test markdown',
runsEvery,
lookBack,
indicatorIndexPattern: ['threat-indicator-*'],
indicatorMapping: 'agent.id',
indicatorIndexField: 'agent.threat',
indicatorIndexPattern: ['filebeat-*'],
indicatorMappingField: 'myhash.mysha256',
indicatorIndexField: 'threatintel.indicator.file.hash.sha256',
type: 'file',
atomic: 'a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3',
timeline,
maxSignals: 100,
};
Expand Down
12 changes: 12 additions & 0 deletions x-pack/plugins/security_solution/cypress/screens/alerts_details.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* 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.
*/

export const JSON_CONTENT = '[data-test-subj="jsonView"]';

export const JSON_LINES = '.ace_line';

export const JSON_VIEW_TAB = '[data-test-subj="jsonViewTab"]';
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
* 2.0.
*/

export const CLOSE_BTN = '[data-test-subj="close"]';

export const FIELDS_BROWSER_CATEGORIES_COUNT = '[data-test-subj="categories-count"]';

export const FIELDS_BROWSER_CHECKBOX = (id: string) => {
return `[data-test-subj="field-${id}-checkbox`;
return `[data-test-subj="category-table-container"] [data-test-subj="field-${id}-checkbox"]`;
};

export const FIELDS_BROWSER_CONTAINER = '[data-test-subj="fields-browser-container"]';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ export const MACHINE_LEARNING_JOB_STATUS = '[data-test-subj="machineLearningJobS

export const MITRE_ATTACK_DETAILS = 'MITRE ATT&CK';

export const FIELDS_BROWSER_BTN =
'[data-test-subj="events-viewer-panel"] [data-test-subj="show-field-browser"]';

export const REFRESH_BUTTON = '[data-test-subj="refreshButton"]';

export const RULE_ABOUT_DETAILS_HEADER_TOGGLE = '[data-test-subj="stepAboutDetailsToggle"]';
Expand Down Expand Up @@ -92,6 +95,10 @@ export const TIMELINE_TEMPLATE_DETAILS = 'Timeline template';

export const TIMESTAMP_OVERRIDE_DETAILS = 'Timestamp override';

export const TIMELINE_FIELD = (field: string) => {
return `[data-test-subj="draggable-content-${field}"]`;
};

export const getDetails = (title: string) =>
cy.get(DETAILS_TITLE).contains(title).next(DETAILS_DESCRIPTION);

Expand Down
17 changes: 17 additions & 0 deletions x-pack/plugins/security_solution/cypress/tasks/alerts_details.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* 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 { JSON_CONTENT, JSON_VIEW_TAB } from '../screens/alerts_details';

export const openJsonView = () => {
cy.get(JSON_VIEW_TAB).click();
};

export const scrollJsonViewToBottom = () => {
cy.get(JSON_CONTENT).click({ force: true });
cy.get(JSON_CONTENT).type('{pagedown}{pagedown}{pagedown}');
};
Original file line number Diff line number Diff line change
Expand Up @@ -45,23 +45,23 @@ export const createCustomIndicatorRule = (rule: ThreatIndicatorRule, ruleId = 'r
{
entries: [
{
field: rule.indicatorMapping,
field: rule.indicatorMappingField,
type: 'mapping',
value: rule.indicatorMapping,
value: rule.indicatorIndexField,
},
],
},
],
threat_query: '*:*',
threat_language: 'kuery',
threat_filters: [],
threat_index: ['mock*'],
threat_index: rule.indicatorIndexPattern,
threat_indicator_path: '',
from: 'now-17520h',
index: ['exceptions-*'],
index: rule.index,
query: rule.customQuery || '*:*',
language: 'kuery',
enabled: false,
enabled: true,
},
headers: { 'kbn-xsrf': 'cypress-creds' },
failOnStatusCode: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ export const getCustomQueryInvalidationText = () => cy.contains(CUSTOM_QUERY_REQ
export const fillDefineIndicatorMatchRuleAndContinue = (rule: ThreatIndicatorRule) => {
fillIndexAndIndicatorIndexPattern(rule.index, rule.indicatorIndexPattern);
fillIndicatorMatchRow({
indexField: rule.indicatorMapping,
indexField: rule.indicatorMappingField,
indicatorIndexField: rule.indicatorIndexField,
});
getDefineContinueButton().should('exist').click({ force: true });
Expand Down
Loading

0 comments on commit 6a57148

Please sign in to comment.