[Detection Engine][Cypress] Reenable and reorganize some detection_alerts and data_sources tests (#168426)

## Summary

#### Code update:
`/detection_engine/routes/signals/set_alert_tags_route.ts`
- When enabling the alert tags cypress test, noticed it was failing as
the tag updates did not show in the UI until a few seconds later upon a
second refresh. I was able to recreate this locally on serverless, not
on ESS. I updated the alerts tag route to include `refresh: true` and
that seemed to fix this issue.
- `/detection_engine/routes/signals/open_close_signals_route.ts`
- When testing on serverless, alert status was stale after update.
Confirmed this with tests that were failing for ESS. Upon updating route
to use `refresh: true`, tests began passing and could see expected
behavior. This may make the call a bit heavier so we will want to see if
there are any performance impacts.
This commit is contained in:
Yara Tercero 2023-10-13 12:34:58 -07:00 committed by GitHub
parent a9a8c96d95
commit 6d3449162d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 1051 additions and 989 deletions

View file

@ -63,7 +63,7 @@ steps:
queue: n2-4-spot
depends_on: build
timeout_in_minutes: 60
parallelism: 2
parallelism: 4
retry:
automatic:
- exit_status: '*'

5
.github/CODEOWNERS vendored
View file

@ -1159,6 +1159,7 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib
/x-pack/plugins/security_solution/common/types/timeline @elastic/security-threat-hunting-investigations
/x-pack/test/security_solution_cypress/cypress/e2e/investigations @elastic/security-threat-hunting-investigations
/x-pack/test/security_solution_cypress/cypress/e2e/sourcerer/sourcerer_timeline.cy.ts @elastic/security-threat-hunting-investigations
/x-pack/plugins/security_solution/public/common/components/alerts_viewer @elastic/security-threat-hunting-investigations
/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_action @elastic/security-threat-hunting-investigations
@ -1297,8 +1298,8 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib
/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals @elastic/security-detection-engine
/x-pack/plugins/security_solution/server/lib/sourcerer @elastic/security-detection-engine
/x-pack/test/security_solution_cypress/cypress/e2e/data_sources @elastic/security-detection-engine
/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts @elastic/security-detection-engine
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/sourcerer @elastic/security-detection-engine
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_alerts @elastic/security-detection-engine
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_actions @elastic/security-detection-engine
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation @elastic/security-detection-engine
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_edit @elastic/security-detection-engine

View file

@ -131,7 +131,7 @@ const updateSignalsStatusByIds = async (
) =>
esClient.updateByQuery({
index: `${DEFAULT_ALERTS_INDEX}-${spaceId}`,
refresh: false,
refresh: true,
body: {
script: getUpdateSignalStatusScript(status),
query: {
@ -158,7 +158,7 @@ const updateSignalsStatusByQuery = async (
esClient.updateByQuery({
index: `${DEFAULT_ALERTS_INDEX}-${spaceId}`,
conflicts: options.conflicts,
refresh: false,
refresh: true,
body: {
script: getUpdateSignalStatusScript(status),
query: {

View file

@ -100,7 +100,7 @@ export const setAlertTagsRoute = (router: SecuritySolutionPluginRouter) => {
try {
const body = await esClient.updateByQuery({
index: `${DEFAULT_ALERTS_INDEX}-${spaceId}`,
refresh: false,
refresh: true,
body: {
script: painlessScript,
query: {

View file

@ -1,63 +0,0 @@
/*
* 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 { login } from '../../tasks/login';
import { visitWithTimeRange } from '../../tasks/navigation';
import { openTimelineUsingToggle } from '../../tasks/security_main';
import { openTimelineFieldsBrowser, populateTimeline } from '../../tasks/timeline';
import { hostsUrl, ALERTS_URL } from '../../urls/navigation';
import { createRule } from '../../tasks/api_calls/rules';
import { getNewRule } from '../../objects/rule';
import { refreshPage } from '../../tasks/security_header';
import { waitForAlertsToPopulate } from '../../tasks/create_new_rule';
import { createField } from '../../tasks/create_runtime_field';
import { openAlertsFieldBrowser } from '../../tasks/alerts';
import { deleteRuntimeField } from '../../tasks/sourcerer';
import { GET_DATA_GRID_HEADER } from '../../screens/common/data_grid';
import { GET_TIMELINE_HEADER } from '../../screens/timeline';
const alertRunTimeField = 'field.name.alert.page';
const timelineRuntimeField = 'field.name.timeline';
// TODO: https://github.com/elastic/kibana/issues/161539
describe(
'Create DataView runtime field',
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
() => {
before(() => {
deleteRuntimeField('security-solution-default', alertRunTimeField);
deleteRuntimeField('security-solution-default', timelineRuntimeField);
});
beforeEach(() => {
login();
});
it('adds field to alert table', () => {
visitWithTimeRange(ALERTS_URL);
createRule(getNewRule());
refreshPage();
waitForAlertsToPopulate();
openAlertsFieldBrowser();
createField(alertRunTimeField);
cy.get(GET_DATA_GRID_HEADER(alertRunTimeField)).should('exist');
});
it('adds field to timeline', () => {
visitWithTimeRange(hostsUrl('allHosts'));
openTimelineUsingToggle();
populateTimeline();
openTimelineFieldsBrowser();
createField(timelineRuntimeField);
cy.get(GET_TIMELINE_HEADER(timelineRuntimeField)).should('exist');
});
}
);

View file

@ -1,136 +0,0 @@
/*
* 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 { DEFAULT_INDEX_PATTERN } from '@kbn/security-solution-plugin/common/constants';
import { login, loginWithUser } from '../../tasks/login';
import { visitWithTimeRange, visitWithUser } from '../../tasks/navigation';
import { hostsUrl } from '../../urls/navigation';
import {
addIndexToDefault,
deselectSourcererOptions,
isDataViewSelection,
isHostsStatValue,
isKibanaDataViewOption,
isNotSourcererSelection,
isSourcererOptions,
isSourcererSelection,
openAdvancedSettings,
openDataViewSelection,
openSourcerer,
resetSourcerer,
saveSourcerer,
} from '../../tasks/sourcerer';
import { postDataView } from '../../tasks/common';
import { createUsersAndRoles, secReadCasesAll, secReadCasesAllUser } from '../../tasks/privileges';
import { TOASTER } from '../../screens/configure_cases';
import { SOURCERER } from '../../screens/sourcerer';
const usersToCreate = [secReadCasesAllUser];
const rolesToCreate = [secReadCasesAll];
const siemDataViewTitle = 'Security Default Data View';
const dataViews = ['auditbeat-*,fakebeat-*', 'auditbeat-*,*beat*,siem-read*,.kibana*,fakebeat-*'];
// TODO: https://github.com/elastic/kibana/issues/161539
describe('Sourcerer', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => {
before(() => {
cy.task('esArchiverResetKibana');
dataViews.forEach((dataView: string) => postDataView(dataView));
});
// TODO: https://github.com/elastic/kibana/issues/161539
describe('permissions', { tags: ['@ess', '@brokenInServerless'] }, () => {
before(() => {
createUsersAndRoles(usersToCreate, rolesToCreate);
});
it(`role(s) ${secReadCasesAllUser.roles.join()} shows error when user does not have permissions`, () => {
loginWithUser(secReadCasesAllUser);
visitWithUser(hostsUrl('allHosts'), secReadCasesAllUser);
cy.get(TOASTER).should('have.text', 'Write role required to generate data');
});
});
// TODO: https://github.com/elastic/kibana/issues/161539
// FLAKY: https://github.com/elastic/kibana/issues/165766
describe('Default scope', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
beforeEach(() => {
cy.clearLocalStorage();
login();
visitWithTimeRange(hostsUrl('allHosts'));
});
it('correctly loads SIEM data view', () => {
openSourcerer();
isDataViewSelection(siemDataViewTitle);
openAdvancedSettings();
isSourcererSelection(`auditbeat-*`);
isSourcererOptions(DEFAULT_INDEX_PATTERN.filter((pattern) => pattern !== 'auditbeat-*'));
});
describe('Modified badge', () => {
it('Selecting new data view does not add a modified badge', () => {
cy.get(SOURCERER.badgeModified).should(`not.exist`);
openSourcerer();
cy.get(SOURCERER.badgeModifiedOption).should(`not.exist`);
openDataViewSelection();
isKibanaDataViewOption(dataViews);
cy.get(SOURCERER.selectListDefaultOption).should(`contain`, siemDataViewTitle);
cy.get(SOURCERER.selectListOption).contains(dataViews[1]).click();
isDataViewSelection(dataViews[1]);
saveSourcerer();
cy.get(SOURCERER.badgeModified).should(`not.exist`);
openSourcerer();
cy.get(SOURCERER.badgeModifiedOption).should(`not.exist`);
});
it('shows modified badge when index patterns change and removes when reset', () => {
openSourcerer();
openDataViewSelection();
cy.get(SOURCERER.selectListOption).contains(dataViews[1]).click();
isDataViewSelection(dataViews[1]);
openAdvancedSettings();
const patterns = dataViews[1].split(',');
deselectSourcererOptions([patterns[0]]);
saveSourcerer();
cy.get(SOURCERER.badgeModified).should(`exist`);
openSourcerer();
cy.get(SOURCERER.badgeModifiedOption).should(`exist`);
resetSourcerer();
saveSourcerer();
cy.get(SOURCERER.badgeModified).should(`not.exist`);
openSourcerer();
cy.get(SOURCERER.badgeModifiedOption).should(`not.exist`);
isDataViewSelection(siemDataViewTitle);
});
});
it('disables save when no patterns are selected', () => {
openSourcerer();
openAdvancedSettings();
cy.get(SOURCERER.saveButton).should('be.enabled');
deselectSourcererOptions(['auditbeat-*']);
cy.get(SOURCERER.saveButton).should('be.disabled');
});
it(
'adds a pattern to the default index and correctly filters out auditbeat-*',
{ tags: '@brokenInServerless' },
() => {
openSourcerer();
isSourcererSelection(`auditbeat-*`);
isNotSourcererSelection('*beat*');
addIndexToDefault('*beat*');
isHostsStatValue('1');
openSourcerer();
openAdvancedSettings();
isSourcererSelection(`auditbeat-*`);
isSourcererSelection('*beat*');
}
);
});
});

View file

@ -1,169 +0,0 @@
/*
* 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 {
DEFAULT_ALERTS_INDEX,
DEFAULT_INDEX_PATTERN,
} from '@kbn/security-solution-plugin/common/constants';
import { login } from '../../tasks/login';
import { visitWithTimeRange } from '../../tasks/navigation';
import { TIMELINES_URL } from '../../urls/navigation';
import {
clickAlertCheckbox,
deselectSourcererOptions,
isDataViewSelection,
isKibanaDataViewOption,
isNotSourcererOption,
isNotSourcererSelection,
isSourcererOptions,
isSourcererSelection,
openAdvancedSettings,
openDataViewSelection,
openSourcerer,
refreshUntilAlertsIndexExists,
resetSourcerer,
saveSourcerer,
} from '../../tasks/sourcerer';
import { openTimelineUsingToggle } from '../../tasks/security_main';
import { SOURCERER } from '../../screens/sourcerer';
import { createTimeline } from '../../tasks/api_calls/timelines';
import { getTimeline, getTimelineModifiedSourcerer } from '../../objects/timeline';
import { closeTimeline, openTimelineById } from '../../tasks/timeline';
const siemDataViewTitle = 'Security Default Data View';
const dataViews = ['auditbeat-*,fakebeat-*', 'auditbeat-*,*beat*,siem-read*,.kibana*,fakebeat-*'];
// TODO: https://github.com/elastic/kibana/issues/161539
describe('Timeline scope', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
beforeEach(() => {
cy.clearLocalStorage();
login();
visitWithTimeRange(TIMELINES_URL);
});
it('correctly loads SIEM data view', () => {
openTimelineUsingToggle();
openSourcerer('timeline');
isDataViewSelection(siemDataViewTitle);
openAdvancedSettings();
isSourcererSelection(`auditbeat-*`);
isSourcererSelection(`${DEFAULT_ALERTS_INDEX}-default`);
isSourcererOptions(DEFAULT_INDEX_PATTERN.filter((pattern) => pattern !== 'auditbeat-*'));
isNotSourcererOption(`${DEFAULT_ALERTS_INDEX}-default`);
});
describe('Modified badge', () => {
it('Selecting new data view does not add a modified badge', () => {
openTimelineUsingToggle();
cy.get(SOURCERER.badgeModified).should(`not.exist`);
openSourcerer('timeline');
cy.get(SOURCERER.badgeModifiedOption).should(`not.exist`);
openDataViewSelection();
isKibanaDataViewOption(dataViews);
cy.get(SOURCERER.selectListDefaultOption).should(`contain`, siemDataViewTitle);
cy.get(SOURCERER.selectListOption).contains(dataViews[1]).click();
isDataViewSelection(dataViews[1]);
saveSourcerer();
cy.get(SOURCERER.badgeModified).should(`not.exist`);
openSourcerer('timeline');
cy.get(SOURCERER.badgeModifiedOption).should(`not.exist`);
});
it('shows modified badge when index patterns change and removes when reset', () => {
openTimelineUsingToggle();
openSourcerer('timeline');
openDataViewSelection();
cy.get(SOURCERER.selectListOption).contains(dataViews[1]).click();
isDataViewSelection(dataViews[1]);
openAdvancedSettings();
const patterns = dataViews[1].split(',');
deselectSourcererOptions([patterns[0]]);
saveSourcerer();
cy.get(SOURCERER.badgeModified).should(`exist`);
openSourcerer('timeline');
cy.get(SOURCERER.badgeModifiedOption).should(`exist`);
resetSourcerer();
saveSourcerer();
cy.get(SOURCERER.badgeModified).should(`not.exist`);
openSourcerer('timeline');
cy.get(SOURCERER.badgeModifiedOption).should(`not.exist`);
isDataViewSelection(siemDataViewTitle);
});
});
describe('Alerts checkbox', () => {
before(() => {
login();
createTimeline(getTimeline()).then((response) =>
cy.wrap(response.body.data.persistTimeline.timeline.savedObjectId).as('timelineId')
);
createTimeline(getTimelineModifiedSourcerer()).then((response) =>
cy.wrap(response.body.data.persistTimeline.timeline.savedObjectId).as('auditbeatTimelineId')
);
});
beforeEach(() => {
login();
visitWithTimeRange(TIMELINES_URL);
refreshUntilAlertsIndexExists();
});
it('Modifies timeline to alerts only, and switches to different saved timeline without issue', function () {
openTimelineById(this.timelineId).then(() => {
cy.get(SOURCERER.badgeAlerts).should(`not.exist`);
cy.get(SOURCERER.badgeModified).should(`not.exist`);
openSourcerer('timeline');
clickAlertCheckbox();
saveSourcerer();
cy.get(SOURCERER.badgeAlerts).should(`exist`);
cy.get(SOURCERER.badgeModified).should(`not.exist`);
closeTimeline();
openTimelineById(this.auditbeatTimelineId).then(() => {
cy.get(SOURCERER.badgeModified).should(`exist`);
cy.get(SOURCERER.badgeAlerts).should(`not.exist`);
openSourcerer('timeline');
openAdvancedSettings();
isSourcererSelection(`auditbeat-*`);
});
});
});
const defaultPatterns = [`auditbeat-*`, `${DEFAULT_ALERTS_INDEX}-default`];
it('alerts checkbox behaves as expected', () => {
isDataViewSelection(siemDataViewTitle);
defaultPatterns.forEach((pattern) => isSourcererSelection(pattern));
openDataViewSelection();
cy.get(SOURCERER.selectListOption).contains(dataViews[1]).click();
isDataViewSelection(dataViews[1]);
dataViews[1]
.split(',')
.filter((pattern) => pattern !== 'fakebeat-*' && pattern !== 'siem-read*')
.forEach((pattern) => isSourcererSelection(pattern));
clickAlertCheckbox();
isNotSourcererSelection(`auditbeat-*`);
isSourcererSelection(`${DEFAULT_ALERTS_INDEX}-default`);
cy.get(SOURCERER.alertCheckbox).uncheck({ force: true });
defaultPatterns.forEach((pattern) => isSourcererSelection(pattern));
});
it('shows alerts badge when index patterns change and removes when reset', () => {
clickAlertCheckbox();
saveSourcerer();
cy.get(SOURCERER.badgeAlerts).should(`exist`);
openSourcerer('timeline');
cy.get(SOURCERER.badgeAlertsOption).should(`exist`);
resetSourcerer();
saveSourcerer();
cy.get(SOURCERER.badgeAlerts).should(`not.exist`);
openSourcerer('timeline');
cy.get(SOURCERER.badgeAlertsOption).should(`not.exist`);
});
});
});

View file

@ -1,197 +0,0 @@
/*
* 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 { disableExpandableFlyout } from '../../tasks/api_calls/kibana_advanced_settings';
import { getNewThreatIndicatorRule, indicatorRuleMatchingDoc } from '../../objects/rule';
import { cleanKibana } from '../../tasks/common';
import { login } from '../../tasks/login';
import {
JSON_TEXT,
TABLE_CELL,
TABLE_ROWS,
THREAT_DETAILS_VIEW,
ENRICHMENT_COUNT_NOTIFICATION,
INDICATOR_MATCH_ENRICHMENT_SECTION,
INVESTIGATION_TIME_ENRICHMENT_SECTION,
THREAT_DETAILS_ACCORDION,
} from '../../screens/alerts_details';
import { TIMELINE_FIELD } from '../../screens/rule_details';
import { expandFirstAlert, setEnrichmentDates, viewThreatIntelTab } from '../../tasks/alerts';
import { createRule } from '../../tasks/api_calls/rules';
import { openJsonView, openThreatIndicatorDetails } from '../../tasks/alerts_details';
import { addsFieldsToTimeline, visitRuleDetailsPage } from '../../tasks/rule_details';
// TODO: https://github.com/elastic/kibana/issues/161539
describe('CTI Enrichment', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
before(() => {
cleanKibana();
// illegal_argument_exception: unknown setting [index.lifecycle.rollover_alias]
cy.task('esArchiverLoad', { archiveName: 'threat_indicator' });
cy.task('esArchiverLoad', { archiveName: 'suspicious_source_event' });
login();
disableExpandableFlyout();
});
after(() => {
cy.task('esArchiverUnload', 'threat_indicator');
cy.task('esArchiverUnload', 'suspicious_source_event');
});
beforeEach(() => {
login();
createRule({ ...getNewThreatIndicatorRule(), rule_id: 'rule_testing', enabled: true }).then(
(rule) => visitRuleDetailsPage(rule.body.id)
);
});
// TODO: https://github.com/elastic/kibana/issues/161539
// Skipped: https://github.com/elastic/kibana/issues/162818
it.skip('Displays enrichment matched.* fields on the timeline', () => {
const expectedFields = {
'threat.enrichments.matched.atomic': indicatorRuleMatchingDoc.atomic,
'threat.enrichments.matched.type': indicatorRuleMatchingDoc.matchedType,
'threat.enrichments.matched.field':
getNewThreatIndicatorRule().threat_mapping[0].entries[0].field,
'threat.enrichments.matched.id': indicatorRuleMatchingDoc.matchedId,
'threat.enrichments.matched.index': indicatorRuleMatchingDoc.matchedIndex,
};
const fields = Object.keys(expectedFields) as Array<keyof typeof expectedFields>;
addsFieldsToTimeline('threat.enrichments.matched', fields);
fields.forEach((field) => {
cy.get(TIMELINE_FIELD(field)).should('have.text', expectedFields[field]);
});
});
it('Displays persisted enrichments on the JSON view', () => {
const expectedEnrichment = [
{
'indicator.file.hash.md5': ['9b6c3518a91d23ed77504b5416bfb5b3'],
'matched.index': ['logs-ti_abusech.malware'],
'indicator.file.type': ['elf'],
'indicator.file.hash.tlsh': [
'6D7312E017B517CC1371A8353BED205E9128223972AE35302E97528DF957703BAB2DBE',
],
'feed.name': ['AbuseCH malware'],
'indicator.file.hash.ssdeep': [
'1536:87vbq1lGAXSEYQjbChaAU2yU23M51DjZgSQAvcYkFtZTjzBht5:8D+CAXFYQChaAUk5ljnQssL',
],
'indicator.file.hash.sha256': [
'a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3',
],
'indicator.first_seen': ['2021-03-10T08:02:14.000Z'],
'matched.field': ['myhash.mysha256'],
'indicator.type': ['file'],
'matched.type': ['indicator_match_rule'],
'matched.id': ['84cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f'],
'matched.atomic': ['a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3'],
'indicator.file.size': [80280],
},
];
expandFirstAlert();
openJsonView();
cy.get(JSON_TEXT).then((x) => {
const parsed = JSON.parse(x.text());
expect(parsed.fields['threat.enrichments']).to.deep.equal(expectedEnrichment);
});
});
it('Displays threat indicator details on the threat intel tab', () => {
const expectedThreatIndicatorData = [
{ field: 'feed.name', value: 'AbuseCH malware' },
{ field: 'indicator.file.hash.md5', value: '9b6c3518a91d23ed77504b5416bfb5b3' },
{
field: 'indicator.file.hash.sha256',
value: 'a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3',
},
{
field: 'indicator.file.hash.ssdeep',
value: '1536:87vbq1lGAXSEYQjbChaAU2yU23M51DjZgSQAvcYkFtZTjzBht5:8D+CAXFYQChaAUk5ljnQssL',
},
{
field: 'indicator.file.hash.tlsh',
value: '6D7312E017B517CC1371A8353BED205E9128223972AE35302E97528DF957703BAB2DBE',
},
{ field: 'indicator.file.size', value: '80280' },
{ field: 'indicator.file.type', value: 'elf' },
{ field: 'indicator.first_seen', value: '2021-03-10T08:02:14.000Z' },
{ field: 'indicator.type', value: 'file' },
{
field: 'matched.atomic',
value: 'a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3',
},
{ field: 'matched.field', value: 'myhash.mysha256' },
{
field: 'matched.id',
value: '84cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f',
},
{ field: 'matched.index', value: 'logs-ti_abusech.malware' },
{ field: 'matched.type', value: 'indicator_match_rule' },
];
expandFirstAlert();
openThreatIndicatorDetails();
cy.get(ENRICHMENT_COUNT_NOTIFICATION).should('have.text', '1');
cy.get(THREAT_DETAILS_VIEW).within(() => {
cy.get(TABLE_ROWS).should('have.length', expectedThreatIndicatorData.length);
expectedThreatIndicatorData.forEach((row, index) => {
cy.get(TABLE_ROWS)
.eq(index)
.within(() => {
cy.get(TABLE_CELL).eq(0).should('have.text', row.field);
cy.get(TABLE_CELL).eq(1).should('have.text', row.value);
});
});
});
});
describe('with additional indicators', () => {
before(() => {
cy.task('esArchiverLoad', { archiveName: 'threat_indicator2' });
});
after(() => {
cy.task('esArchiverUnload', 'threat_indicator2');
});
it('Displays matched fields from both indicator match rules and investigation time enrichments on Threat Intel tab', () => {
const indicatorMatchRuleEnrichment = {
field: 'myhash.mysha256',
value: 'a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3',
feedName: 'AbuseCH malware',
};
const investigationTimeEnrichment = {
field: 'source.ip',
value: '192.168.1.1',
feedName: 'feed_name',
};
expandFirstAlert();
viewThreatIntelTab();
setEnrichmentDates('08/05/2018 10:00 AM');
cy.get(`${INDICATOR_MATCH_ENRICHMENT_SECTION} ${THREAT_DETAILS_ACCORDION}`)
.should('exist')
.should(
'have.text',
`${indicatorMatchRuleEnrichment.field} ${indicatorMatchRuleEnrichment.value} from ${indicatorMatchRuleEnrichment.feedName}`
);
cy.get(`${INVESTIGATION_TIME_ENRICHMENT_SECTION} ${THREAT_DETAILS_ACCORDION}`)
.should('exist')
.should(
'have.text',
`${investigationTimeEnrichment.field} ${investigationTimeEnrichment.value} from ${investigationTimeEnrichment.feedName}`
);
});
});
});

View file

@ -1,73 +0,0 @@
/*
* 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 { waitForAlertsToPopulate } from '../../tasks/create_new_rule';
import { login } from '../../tasks/login';
import { visitWithTimeRange } from '../../tasks/navigation';
import { ALERTS_URL, TIMELINES_URL } from '../../urls/navigation';
import { ALERTS_HISTOGRAM_SERIES, ALERT_RULE_NAME, MESSAGE } from '../../screens/alerts';
import { TIMELINE_QUERY, TIMELINE_VIEW_IN_ANALYZER } from '../../screens/timeline';
import { selectAlertsHistogram } from '../../tasks/alerts';
import { createTimeline } from '../../tasks/timelines';
// TODO: https://github.com/elastic/kibana/issues/161539
describe(
'Ransomware Detection Alerts',
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
() => {
before(() => {
cy.task('esArchiverLoad', {
archiveName: 'ransomware_detection',
useCreate: true,
docsOnly: true,
});
});
describe('Ransomware display in Alerts Section', () => {
beforeEach(() => {
login();
visitWithTimeRange(ALERTS_URL);
waitForAlertsToPopulate();
});
describe('Alerts table', () => {
it('shows Ransomware Alerts', () => {
cy.get(ALERT_RULE_NAME).should('have.text', 'Ransomware Detection Alert');
});
});
describe('Trend Chart', () => {
beforeEach(() => {
selectAlertsHistogram();
});
it('shows Ransomware Detection Alert in the trend chart', () => {
cy.get(ALERTS_HISTOGRAM_SERIES).should('have.text', 'Ransomware Detection Alert');
});
});
});
// FLAKY: https://github.com/elastic/kibana/issues/168602
describe.skip('Ransomware in Timelines', () => {
before(() => {
login();
visitWithTimeRange(TIMELINES_URL);
createTimeline();
});
it('Renders ransomware entries in timelines table', () => {
cy.get(TIMELINE_QUERY).type('event.code: "ransomware"{enter}');
// Wait for grid to load, it should have an analyzer icon
cy.get(TIMELINE_VIEW_IN_ANALYZER).should('exist');
cy.get(MESSAGE).should('have.text', 'Ransomware Detection Alert');
});
});
}
);

View file

@ -1,79 +0,0 @@
/*
* 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 { waitForAlertsToPopulate } from '../../tasks/create_new_rule';
import { login } from '../../tasks/login';
import { visitWithTimeRange } from '../../tasks/navigation';
import { ALERTS_URL, TIMELINES_URL } from '../../urls/navigation';
import { ALERTS_HISTOGRAM_SERIES, ALERT_RULE_NAME, MESSAGE } from '../../screens/alerts';
import { TIMELINE_QUERY, TIMELINE_VIEW_IN_ANALYZER } from '../../screens/timeline';
import { selectAlertsHistogram } from '../../tasks/alerts';
import { createTimeline } from '../../tasks/timelines';
import { cleanKibana } from '../../tasks/common';
// TODO: https://github.com/elastic/kibana/issues/161539
describe(
'Ransomware Prevention Alerts',
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
() => {
before(() => {
cleanKibana();
cy.task('esArchiverLoad', {
archiveName: 'ransomware_prevention',
useCreate: true,
docsOnly: true,
});
});
after(() => {
cy.task('esArchiverUnload', 'ransomware_prevention');
});
describe('Ransomware display in Alerts Section', () => {
beforeEach(() => {
login();
visitWithTimeRange(ALERTS_URL);
waitForAlertsToPopulate();
});
describe('Alerts table', () => {
it('shows Ransomware Alerts', () => {
cy.get(ALERT_RULE_NAME).should('have.text', 'Ransomware Prevention Alert');
});
});
describe('Trend Chart', () => {
beforeEach(() => {
selectAlertsHistogram();
});
it('shows Ransomware Prevention Alert in the trend chart', () => {
cy.get(ALERTS_HISTOGRAM_SERIES).should('have.text', 'Ransomware Prevention Alert');
});
});
});
describe('Ransomware in Timelines', () => {
beforeEach(() => {
login();
visitWithTimeRange(TIMELINES_URL);
createTimeline();
});
it('Renders ransomware entries in timelines table', () => {
cy.get(TIMELINE_QUERY).type('event.code: "ransomware"{enter}');
// Wait for grid to load, it should have an analyzer icon
cy.get(TIMELINE_VIEW_IN_ANALYZER).should('exist');
cy.get(MESSAGE).should('have.text', 'Ransomware Prevention Alert');
});
});
}
);

View file

@ -0,0 +1,230 @@
/*
* 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 { getNewRule } from '../../../objects/rule';
import { ALERTS_COUNT, SELECTED_ALERTS } from '../../../screens/alerts';
import {
selectNumberOfAlerts,
waitForAlerts,
markAcknowledgedFirstAlert,
markAlertsAcknowledged,
goToAcknowledgedAlerts,
closeFirstAlert,
closeAlerts,
goToClosedAlerts,
goToOpenedAlerts,
openAlerts,
openFirstAlert,
} from '../../../tasks/alerts';
import { createRule } from '../../../tasks/api_calls/rules';
import { deleteAlertsAndRules } from '../../../tasks/common';
import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule';
import { login } from '../../../tasks/login';
import { visit } from '../../../tasks/navigation';
import { ALERTS_URL } from '../../../urls/navigation';
describe('Changing alert status', { tags: ['@ess', '@serverless'] }, () => {
before(() => {
cy.task('esArchiverLoad', { archiveName: 'auditbeat_big' });
});
context('Opening alerts', () => {
beforeEach(() => {
login();
deleteAlertsAndRules();
createRule(getNewRule());
visit(ALERTS_URL);
waitForAlertsToPopulate();
selectNumberOfAlerts(3);
cy.get(SELECTED_ALERTS).should('have.text', `Selected 3 alerts`);
closeAlerts();
waitForAlerts();
});
after(() => {
cy.task('esArchiverUnload', 'auditbeat_big');
});
it('can mark a closed alert as open', () => {
waitForAlertsToPopulate();
cy.get(ALERTS_COUNT)
.invoke('text')
.then((numberOfOpenedAlertsText) => {
const numberOfOpenedAlerts = parseInt(numberOfOpenedAlertsText, 10);
goToClosedAlerts();
waitForAlerts();
cy.get(ALERTS_COUNT)
.invoke('text')
.then((alertNumberString) => {
const numberOfAlerts = alertNumberString.split(' ')[0];
const numberOfAlertsToBeOpened = 1;
openFirstAlert();
waitForAlerts();
const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeOpened;
cy.get(ALERTS_COUNT).contains(expectedNumberOfAlerts);
goToOpenedAlerts();
waitForAlerts();
cy.get(ALERTS_COUNT).contains(`${numberOfOpenedAlerts + numberOfAlertsToBeOpened}`);
});
});
});
it('can bulk open alerts', () => {
waitForAlertsToPopulate();
cy.get(ALERTS_COUNT)
.invoke('text')
.then((numberOfOpenedAlertsText) => {
const numberOfOpenedAlerts = parseInt(numberOfOpenedAlertsText, 10);
goToClosedAlerts();
waitForAlerts();
cy.get(ALERTS_COUNT)
.invoke('text')
.then((alertNumberString) => {
const numberOfAlerts = alertNumberString.split(' ')[0];
const numberOfAlertsToBeOpened = 2;
const numberOfAlertsToBeSelected = 2;
selectNumberOfAlerts(numberOfAlertsToBeSelected);
cy.get(SELECTED_ALERTS).should(
'have.text',
`Selected ${numberOfAlertsToBeSelected} alerts`
);
openAlerts();
waitForAlerts();
const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeOpened;
cy.get(ALERTS_COUNT).contains(expectedNumberOfAlerts);
goToOpenedAlerts();
waitForAlerts();
cy.get(ALERTS_COUNT).contains(`${numberOfOpenedAlerts + numberOfAlertsToBeOpened}`);
});
});
});
});
context('Marking alerts as acknowledged', () => {
beforeEach(() => {
login();
deleteAlertsAndRules();
createRule(getNewRule());
visit(ALERTS_URL);
waitForAlertsToPopulate();
});
it('can mark alert as acknowledged', () => {
cy.get(ALERTS_COUNT)
.invoke('text')
.then((alertNumberString) => {
const numberOfAlerts = alertNumberString.split(' ')[0];
const numberOfAlertsToBeMarkedAcknowledged = 1;
markAcknowledgedFirstAlert();
waitForAlerts();
const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeMarkedAcknowledged;
cy.get(ALERTS_COUNT).contains(expectedNumberOfAlerts);
goToAcknowledgedAlerts();
waitForAlerts();
cy.get(ALERTS_COUNT).contains(`${numberOfAlertsToBeMarkedAcknowledged}`);
});
});
it('can bulk mark alerts as acknowledged', () => {
cy.get(ALERTS_COUNT)
.invoke('text')
.then((alertNumberString) => {
const numberOfAlerts = alertNumberString.split(' ')[0];
const numberOfAlertsToBeMarkedAcknowledged = 2;
const numberOfAlertsToBeSelected = 2;
selectNumberOfAlerts(numberOfAlertsToBeSelected);
markAlertsAcknowledged();
waitForAlerts();
const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeMarkedAcknowledged;
cy.get(ALERTS_COUNT).contains(expectedNumberOfAlerts);
goToAcknowledgedAlerts();
waitForAlerts();
cy.get(ALERTS_COUNT).contains(numberOfAlertsToBeMarkedAcknowledged);
});
});
});
context('Closing alerts', () => {
beforeEach(() => {
login();
deleteAlertsAndRules();
createRule(getNewRule({ rule_id: '1', max_signals: 100 }));
visit(ALERTS_URL);
waitForAlertsToPopulate();
});
it('can close an alert', () => {
const numberOfAlertsToBeClosed = 1;
cy.get(ALERTS_COUNT)
.invoke('text')
.then((alertNumberString) => {
const numberOfAlerts = alertNumberString.split(' ')[0];
cy.get(ALERTS_COUNT).should('have.text', `${numberOfAlerts} alerts`);
selectNumberOfAlerts(numberOfAlertsToBeClosed);
cy.get(SELECTED_ALERTS).should('have.text', `Selected ${numberOfAlertsToBeClosed} alert`);
closeFirstAlert();
waitForAlerts();
const expectedNumberOfAlertsAfterClosing = +numberOfAlerts - numberOfAlertsToBeClosed;
cy.get(ALERTS_COUNT).contains(expectedNumberOfAlertsAfterClosing);
goToClosedAlerts();
waitForAlerts();
cy.get(ALERTS_COUNT).contains(numberOfAlertsToBeClosed);
});
});
it('can bulk close alerts', () => {
const numberOfAlertsToBeClosed = 2;
cy.get(ALERTS_COUNT)
.invoke('text')
.then((alertNumberString) => {
const numberOfAlerts = alertNumberString.split(' ')[0];
cy.get(ALERTS_COUNT).should('have.text', `${numberOfAlerts} alerts`);
selectNumberOfAlerts(numberOfAlertsToBeClosed);
cy.get(SELECTED_ALERTS).should(
'have.text',
`Selected ${numberOfAlertsToBeClosed} alerts`
);
closeAlerts();
waitForAlerts();
const expectedNumberOfAlertsAfterClosing = +numberOfAlerts - numberOfAlertsToBeClosed;
cy.get(ALERTS_COUNT).contains(expectedNumberOfAlertsAfterClosing);
goToClosedAlerts();
waitForAlerts();
cy.get(ALERTS_COUNT).contains(numberOfAlertsToBeClosed);
});
});
});
});

View file

@ -5,28 +5,27 @@
* 2.0.
*/
import { getNewRule } from '../../objects/rule';
import { getNewRule } from '../../../objects/rule';
import {
clickAlertTag,
openAlertTaggingBulkActionMenu,
selectNumberOfAlerts,
updateAlertTags,
} from '../../tasks/alerts';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common';
import { login } from '../../tasks/login';
import { visitWithTimeRange } from '../../tasks/navigation';
import { ALERTS_URL } from '../../urls/navigation';
import { waitForAlertsToPopulate } from '../../tasks/create_new_rule';
} from '../../../tasks/alerts';
import { createRule } from '../../../tasks/api_calls/rules';
import { cleanKibana, deleteAlertsAndRules } from '../../../tasks/common';
import { login } from '../../../tasks/login';
import { visitWithTimeRange } from '../../../tasks/navigation';
import { ALERTS_URL } from '../../../urls/navigation';
import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule';
import {
ALERTS_TABLE_ROW_LOADER,
MIXED_ALERT_TAG,
SELECTED_ALERT_TAG,
UNSELECTED_ALERT_TAG,
} from '../../screens/alerts';
} from '../../../screens/alerts';
// TODO: https://github.com/elastic/kibana/issues/161539
describe('Alert tagging', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
describe('Alert tagging', { tags: ['@ess', '@serverless'] }, () => {
before(() => {
cleanKibana();
cy.task('esArchiverResetKibana');

View file

@ -7,21 +7,21 @@
import { ROLES } from '@kbn/security-solution-plugin/common/test';
import { ALERTS_URL } from '../../urls/navigation';
import { RULES_MANAGEMENT_URL } from '../../urls/rules_management';
import { ruleDetailsUrl } from '../../urls/rule_details';
import { getNewRule } from '../../objects/rule';
import { PAGE_TITLE } from '../../screens/common/page';
import { ALERTS_URL } from '../../../urls/navigation';
import { RULES_MANAGEMENT_URL } from '../../../urls/rules_management';
import { ruleDetailsUrl } from '../../../urls/rule_details';
import { getNewRule } from '../../../objects/rule';
import { PAGE_TITLE } from '../../../screens/common/page';
import { login } from '../../tasks/login';
import { visit } from '../../tasks/navigation';
import { login } from '../../../tasks/login';
import { visit } from '../../../tasks/navigation';
import { createRule, deleteCustomRule } from '../../tasks/api_calls/rules';
import { createRule, deleteCustomRule } from '../../../tasks/api_calls/rules';
import {
getCallOut,
NEED_ADMIN_FOR_UPDATE_CALLOUT,
waitForCallOutToBeShown,
} from '../../tasks/common/callouts';
} from '../../../tasks/common/callouts';
const loadPageAsPlatformEngineerUser = (url: string) => {
login(ROLES.soc_manager);
@ -33,7 +33,6 @@ const waitForPageTitleToBeShown = () => {
cy.get(PAGE_TITLE).should('be.visible');
};
// TODO: https://github.com/elastic/kibana/issues/161539 Does it need to run in Serverless?
describe(
'Detections > Need Admin Callouts indicating an admin is needed to migrate the alert data set',
{ tags: ['@ess', '@skipInServerless'] },

View file

@ -7,21 +7,21 @@
import { ROLES } from '@kbn/security-solution-plugin/common/test';
import { ALERTS_URL } from '../../urls/navigation';
import { RULES_MANAGEMENT_URL } from '../../urls/rules_management';
import { getNewRule } from '../../objects/rule';
import { PAGE_TITLE } from '../../screens/common/page';
import { ALERTS_URL } from '../../../urls/navigation';
import { RULES_MANAGEMENT_URL } from '../../../urls/rules_management';
import { getNewRule } from '../../../objects/rule';
import { PAGE_TITLE } from '../../../screens/common/page';
import { login } from '../../tasks/login';
import { visit } from '../../tasks/navigation';
import { createRule, deleteCustomRule } from '../../tasks/api_calls/rules';
import { login } from '../../../tasks/login';
import { visit } from '../../../tasks/navigation';
import { createRule, deleteCustomRule } from '../../../tasks/api_calls/rules';
import {
getCallOut,
waitForCallOutToBeShown,
dismissCallOut,
MISSING_PRIVILEGES_CALLOUT,
} from '../../tasks/common/callouts';
import { ruleDetailsUrl } from '../../urls/rule_details';
} from '../../../tasks/common/callouts';
import { ruleDetailsUrl } from '../../../urls/rule_details';
const loadPageAsReadOnlyUser = (url: string) => {
login(ROLES.reader);

View file

@ -0,0 +1,201 @@
/*
* 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 { disableExpandableFlyout } from '../../../tasks/api_calls/kibana_advanced_settings';
import { getNewThreatIndicatorRule, indicatorRuleMatchingDoc } from '../../../objects/rule';
import { cleanKibana } from '../../../tasks/common';
import { login } from '../../../tasks/login';
import {
JSON_TEXT,
TABLE_CELL,
TABLE_ROWS,
THREAT_DETAILS_VIEW,
ENRICHMENT_COUNT_NOTIFICATION,
INDICATOR_MATCH_ENRICHMENT_SECTION,
INVESTIGATION_TIME_ENRICHMENT_SECTION,
THREAT_DETAILS_ACCORDION,
} from '../../../screens/alerts_details';
import { TIMELINE_FIELD } from '../../../screens/rule_details';
import { expandFirstAlert, setEnrichmentDates, viewThreatIntelTab } from '../../../tasks/alerts';
import { createRule } from '../../../tasks/api_calls/rules';
import { openJsonView, openThreatIndicatorDetails } from '../../../tasks/alerts_details';
import { addsFieldsToTimeline, visitRuleDetailsPage } from '../../../tasks/rule_details';
// TODO: https://github.com/elastic/kibana/issues/161539
describe(
'Threat Match Enrichment',
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
() => {
before(() => {
cleanKibana();
// illegal_argument_exception: unknown setting [index.lifecycle.rollover_alias]
cy.task('esArchiverLoad', { archiveName: 'threat_indicator' });
cy.task('esArchiverLoad', { archiveName: 'suspicious_source_event' });
login();
disableExpandableFlyout();
});
after(() => {
cy.task('esArchiverUnload', 'threat_indicator');
cy.task('esArchiverUnload', 'suspicious_source_event');
});
beforeEach(() => {
login();
createRule({ ...getNewThreatIndicatorRule(), rule_id: 'rule_testing', enabled: true }).then(
(rule) => visitRuleDetailsPage(rule.body.id)
);
});
// TODO: https://github.com/elastic/kibana/issues/161539
// Skipped: https://github.com/elastic/kibana/issues/162818
it.skip('Displays enrichment matched.* fields on the timeline', () => {
const expectedFields = {
'threat.enrichments.matched.atomic': indicatorRuleMatchingDoc.atomic,
'threat.enrichments.matched.type': indicatorRuleMatchingDoc.matchedType,
'threat.enrichments.matched.field':
getNewThreatIndicatorRule().threat_mapping[0].entries[0].field,
'threat.enrichments.matched.id': indicatorRuleMatchingDoc.matchedId,
'threat.enrichments.matched.index': indicatorRuleMatchingDoc.matchedIndex,
};
const fields = Object.keys(expectedFields) as Array<keyof typeof expectedFields>;
addsFieldsToTimeline('threat.enrichments.matched', fields);
fields.forEach((field) => {
cy.get(TIMELINE_FIELD(field)).should('have.text', expectedFields[field]);
});
});
it('Displays persisted enrichments on the JSON view', () => {
const expectedEnrichment = [
{
'indicator.file.hash.md5': ['9b6c3518a91d23ed77504b5416bfb5b3'],
'matched.index': ['logs-ti_abusech.malware'],
'indicator.file.type': ['elf'],
'indicator.file.hash.tlsh': [
'6D7312E017B517CC1371A8353BED205E9128223972AE35302E97528DF957703BAB2DBE',
],
'feed.name': ['AbuseCH malware'],
'indicator.file.hash.ssdeep': [
'1536:87vbq1lGAXSEYQjbChaAU2yU23M51DjZgSQAvcYkFtZTjzBht5:8D+CAXFYQChaAUk5ljnQssL',
],
'indicator.file.hash.sha256': [
'a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3',
],
'indicator.first_seen': ['2021-03-10T08:02:14.000Z'],
'matched.field': ['myhash.mysha256'],
'indicator.type': ['file'],
'matched.type': ['indicator_match_rule'],
'matched.id': ['84cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f'],
'matched.atomic': ['a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3'],
'indicator.file.size': [80280],
},
];
expandFirstAlert();
openJsonView();
cy.get(JSON_TEXT).then((x) => {
const parsed = JSON.parse(x.text());
expect(parsed.fields['threat.enrichments']).to.deep.equal(expectedEnrichment);
});
});
it('Displays threat indicator details on the threat intel tab', () => {
const expectedThreatIndicatorData = [
{ field: 'feed.name', value: 'AbuseCH malware' },
{ field: 'indicator.file.hash.md5', value: '9b6c3518a91d23ed77504b5416bfb5b3' },
{
field: 'indicator.file.hash.sha256',
value: 'a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3',
},
{
field: 'indicator.file.hash.ssdeep',
value: '1536:87vbq1lGAXSEYQjbChaAU2yU23M51DjZgSQAvcYkFtZTjzBht5:8D+CAXFYQChaAUk5ljnQssL',
},
{
field: 'indicator.file.hash.tlsh',
value: '6D7312E017B517CC1371A8353BED205E9128223972AE35302E97528DF957703BAB2DBE',
},
{ field: 'indicator.file.size', value: '80280' },
{ field: 'indicator.file.type', value: 'elf' },
{ field: 'indicator.first_seen', value: '2021-03-10T08:02:14.000Z' },
{ field: 'indicator.type', value: 'file' },
{
field: 'matched.atomic',
value: 'a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3',
},
{ field: 'matched.field', value: 'myhash.mysha256' },
{
field: 'matched.id',
value: '84cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f',
},
{ field: 'matched.index', value: 'logs-ti_abusech.malware' },
{ field: 'matched.type', value: 'indicator_match_rule' },
];
expandFirstAlert();
openThreatIndicatorDetails();
cy.get(ENRICHMENT_COUNT_NOTIFICATION).should('have.text', '1');
cy.get(THREAT_DETAILS_VIEW).within(() => {
cy.get(TABLE_ROWS).should('have.length', expectedThreatIndicatorData.length);
expectedThreatIndicatorData.forEach((row, index) => {
cy.get(TABLE_ROWS)
.eq(index)
.within(() => {
cy.get(TABLE_CELL).eq(0).should('have.text', row.field);
cy.get(TABLE_CELL).eq(1).should('have.text', row.value);
});
});
});
});
describe('with additional indicators', () => {
before(() => {
cy.task('esArchiverLoad', { archiveName: 'threat_indicator2' });
});
after(() => {
cy.task('esArchiverUnload', 'threat_indicator2');
});
it('Displays matched fields from both indicator match rules and investigation time enrichments on Threat Intel tab', () => {
const indicatorMatchRuleEnrichment = {
field: 'myhash.mysha256',
value: 'a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3',
feedName: 'AbuseCH malware',
};
const investigationTimeEnrichment = {
field: 'source.ip',
value: '192.168.1.1',
feedName: 'feed_name',
};
expandFirstAlert();
viewThreatIntelTab();
setEnrichmentDates('08/05/2018 10:00 AM');
cy.get(`${INDICATOR_MATCH_ENRICHMENT_SECTION} ${THREAT_DETAILS_ACCORDION}`)
.should('exist')
.should(
'have.text',
`${indicatorMatchRuleEnrichment.field} ${indicatorMatchRuleEnrichment.value} from ${indicatorMatchRuleEnrichment.feedName}`
);
cy.get(`${INVESTIGATION_TIME_ENRICHMENT_SECTION} ${THREAT_DETAILS_ACCORDION}`)
.should('exist')
.should(
'have.text',
`${investigationTimeEnrichment.field} ${investigationTimeEnrichment.value} from ${investigationTimeEnrichment.feedName}`
);
});
});
}
);

View file

@ -0,0 +1,58 @@
/*
* 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 { login } from '../../../tasks/login';
import { visitWithTimeRange } from '../../../tasks/navigation';
import { openTimelineUsingToggle } from '../../../tasks/security_main';
import { openTimelineFieldsBrowser, populateTimeline } from '../../../tasks/timeline';
import { hostsUrl, ALERTS_URL } from '../../../urls/navigation';
import { createRule } from '../../../tasks/api_calls/rules';
import { getNewRule } from '../../../objects/rule';
import { refreshPage } from '../../../tasks/security_header';
import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule';
import { createField } from '../../../tasks/create_runtime_field';
import { openAlertsFieldBrowser } from '../../../tasks/alerts';
import { deleteRuntimeField } from '../../../tasks/sourcerer';
import { GET_DATA_GRID_HEADER } from '../../../screens/common/data_grid';
import { GET_TIMELINE_HEADER } from '../../../screens/timeline';
const alertRunTimeField = 'field.name.alert.page';
const timelineRuntimeField = 'field.name.timeline';
describe('Create DataView runtime field', { tags: ['@ess', '@serverless'] }, () => {
before(() => {
deleteRuntimeField('security-solution-default', alertRunTimeField);
deleteRuntimeField('security-solution-default', timelineRuntimeField);
});
beforeEach(() => {
login();
});
it('adds field to alert table', () => {
visitWithTimeRange(ALERTS_URL);
createRule(getNewRule());
refreshPage();
waitForAlertsToPopulate();
openAlertsFieldBrowser();
createField(alertRunTimeField);
cy.get(GET_DATA_GRID_HEADER(alertRunTimeField)).should('exist');
});
it('adds field to timeline', () => {
visitWithTimeRange(hostsUrl('allHosts'));
openTimelineUsingToggle();
populateTimeline();
openTimelineFieldsBrowser();
createField(timelineRuntimeField);
cy.get(GET_TIMELINE_HEADER(timelineRuntimeField)).should('exist');
});
});

View file

@ -0,0 +1,115 @@
/*
* 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 { DEFAULT_INDEX_PATTERN } from '@kbn/security-solution-plugin/common/constants';
import { login } from '../../../tasks/login';
import { visitWithTimeRange } from '../../../tasks/navigation';
import { hostsUrl } from '../../../urls/navigation';
import {
addIndexToDefault,
deselectSourcererOptions,
isDataViewSelection,
isHostsStatValue,
isKibanaDataViewOption,
isNotSourcererSelection,
isSourcererOptions,
isSourcererSelection,
openAdvancedSettings,
openDataViewSelection,
openSourcerer,
resetSourcerer,
saveSourcerer,
} from '../../../tasks/sourcerer';
import { postDataView } from '../../../tasks/common';
import { SOURCERER } from '../../../screens/sourcerer';
const siemDataViewTitle = 'Security Default Data View';
const dataViews = ['auditbeat-*,fakebeat-*', 'auditbeat-*,*beat*,siem-read*,.kibana*,fakebeat-*'];
describe('Sourcerer', { tags: ['@ess', '@serverless'] }, () => {
before(() => {
cy.task('esArchiverResetKibana');
dataViews.forEach((dataView: string) => postDataView(dataView));
});
beforeEach(() => {
cy.clearLocalStorage();
login();
visitWithTimeRange(hostsUrl('allHosts'));
});
it('correctly loads SIEM data view', () => {
openSourcerer();
isDataViewSelection(siemDataViewTitle);
openAdvancedSettings();
isSourcererSelection(`auditbeat-*`);
isSourcererOptions(DEFAULT_INDEX_PATTERN.filter((pattern) => pattern !== 'auditbeat-*'));
});
describe('Modified badge', () => {
it('Selecting new data view does not add a modified badge', () => {
cy.get(SOURCERER.badgeModified).should(`not.exist`);
openSourcerer();
cy.get(SOURCERER.badgeModifiedOption).should(`not.exist`);
openDataViewSelection();
isKibanaDataViewOption(dataViews);
cy.get(SOURCERER.selectListDefaultOption).should(`contain`, siemDataViewTitle);
cy.get(SOURCERER.selectListOption).contains(dataViews[1]).click();
isDataViewSelection(dataViews[1]);
saveSourcerer();
cy.get(SOURCERER.badgeModified).should(`not.exist`);
openSourcerer();
cy.get(SOURCERER.badgeModifiedOption).should(`not.exist`);
});
it('shows modified badge when index patterns change and removes when reset', () => {
openSourcerer();
openDataViewSelection();
cy.get(SOURCERER.selectListOption).contains(dataViews[1]).click();
isDataViewSelection(dataViews[1]);
openAdvancedSettings();
const patterns = dataViews[1].split(',');
deselectSourcererOptions([patterns[0]]);
saveSourcerer();
cy.get(SOURCERER.badgeModified).should(`exist`);
openSourcerer();
cy.get(SOURCERER.badgeModifiedOption).should(`exist`);
resetSourcerer();
saveSourcerer();
cy.get(SOURCERER.badgeModified).should(`not.exist`);
openSourcerer();
cy.get(SOURCERER.badgeModifiedOption).should(`not.exist`);
isDataViewSelection(siemDataViewTitle);
});
});
it('disables save when no patterns are selected', () => {
openSourcerer();
openAdvancedSettings();
cy.get(SOURCERER.saveButton).should('be.enabled');
deselectSourcererOptions(['auditbeat-*']);
cy.get(SOURCERER.saveButton).should('be.disabled');
});
it(
'adds a pattern to the default index and correctly filters out auditbeat-*',
{ tags: '@brokenInServerless' },
() => {
openSourcerer();
isSourcererSelection(`auditbeat-*`);
isNotSourcererSelection('*beat*');
addIndexToDefault('*beat*');
isHostsStatValue('1');
openSourcerer();
openAdvancedSettings();
isSourcererSelection(`auditbeat-*`);
isSourcererSelection('*beat*');
}
);
});

View file

@ -0,0 +1,36 @@
/*
* 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 { loginWithUser } from '../../../tasks/login';
import { visitWithUser } from '../../../tasks/navigation';
import { hostsUrl } from '../../../urls/navigation';
import { postDataView } from '../../../tasks/common';
import {
createUsersAndRoles,
secReadCasesAll,
secReadCasesAllUser,
} from '../../../tasks/privileges';
import { TOASTER } from '../../../screens/configure_cases';
const usersToCreate = [secReadCasesAllUser];
const rolesToCreate = [secReadCasesAll];
const dataViews = ['auditbeat-*,fakebeat-*', 'auditbeat-*,*beat*,siem-read*,.kibana*,fakebeat-*'];
describe('Sourcerer permissions', { tags: ['@ess', '@skipInServerless'] }, () => {
before(() => {
cy.task('esArchiverResetKibana');
dataViews.forEach((dataView: string) => postDataView(dataView));
createUsersAndRoles(usersToCreate, rolesToCreate);
});
it(`role(s) ${secReadCasesAllUser.roles.join()} shows error when user does not have permissions`, () => {
loginWithUser(secReadCasesAllUser);
visitWithUser(hostsUrl('allHosts'), secReadCasesAllUser);
cy.get(TOASTER).should('have.text', 'Write role required to generate data');
});
});

View file

@ -10,10 +10,10 @@ import {
DEFAULT_INDEX_PATTERN,
} from '@kbn/security-solution-plugin/common/constants';
import { login } from '../../tasks/login';
import { visitWithTimeRange } from '../../tasks/navigation';
import { login } from '../../../tasks/login';
import { visitWithTimeRange } from '../../../tasks/navigation';
import { TIMELINES_URL } from '../../urls/navigation';
import { TIMELINES_URL } from '../../../urls/navigation';
import {
clickAlertCheckbox,
deselectSourcererOptions,
@ -29,12 +29,12 @@ import {
refreshUntilAlertsIndexExists,
resetSourcerer,
saveSourcerer,
} from '../../tasks/sourcerer';
import { openTimelineUsingToggle } from '../../tasks/security_main';
import { SOURCERER } from '../../screens/sourcerer';
import { createTimeline } from '../../tasks/api_calls/timelines';
import { getTimeline, getTimelineModifiedSourcerer } from '../../objects/timeline';
import { closeTimeline, openTimelineById } from '../../tasks/timeline';
} from '../../../tasks/sourcerer';
import { openTimelineUsingToggle } from '../../../tasks/security_main';
import { SOURCERER } from '../../../screens/sourcerer';
import { createTimeline } from '../../../tasks/api_calls/timelines';
import { getTimeline, getTimelineModifiedSourcerer } from '../../../objects/timeline';
import { closeTimeline, openTimelineById } from '../../../tasks/timeline';
const siemDataViewTitle = 'Security Default Data View';
const dataViews = ['auditbeat-*,fakebeat-*', 'auditbeat-*,*beat*,siem-read*,.kibana*,fakebeat-*'];

View file

@ -0,0 +1,23 @@
/*
* 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 { ROLES } from '@kbn/security-solution-plugin/common/test';
import { login } from '../../../tasks/login';
import { visit } from '../../../tasks/navigation';
import { RULES_MANAGEMENT_URL } from '../../../urls/rules_management';
import { VALUE_LISTS_MODAL_ACTIVATOR } from '../../../screens/lists';
describe('value list permissions', { tags: ['@ess', '@skipInServerless'] }, () => {
describe('user with restricted access role', () => {
it('Does not allow a t1 analyst user to upload a value list', () => {
login(ROLES.t1_analyst);
visit(RULES_MANAGEMENT_URL, { role: ROLES.t1_analyst });
cy.get(VALUE_LISTS_MODAL_ACTIVATOR).should('have.attr', 'disabled');
});
});
});

View file

@ -5,8 +5,6 @@
* 2.0.
*/
import { ROLES } from '@kbn/security-solution-plugin/common/test';
import { login } from '../../../tasks/login';
import { visit } from '../../../tasks/navigation';
import { RULES_MANAGEMENT_URL } from '../../../urls/rules_management';
@ -25,253 +23,224 @@ import {
deleteValueLists,
KNOWN_VALUE_LIST_FILES,
} from '../../../tasks/lists';
import {
VALUE_LISTS_TABLE,
VALUE_LISTS_ROW,
VALUE_LISTS_MODAL_ACTIVATOR,
} from '../../../screens/lists';
import { VALUE_LISTS_TABLE, VALUE_LISTS_ROW } from '../../../screens/lists';
import { refreshIndex } from '../../../tasks/api_calls/elasticsearch';
// TODO: https://github.com/elastic/kibana/issues/161539
// FLAKY: https://github.com/elastic/kibana/issues/165699
describe('value lists', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => {
// TODO: https://github.com/elastic/kibana/issues/161539
describe('management modal', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
describe('value lists management modal', { tags: ['@ess', '@serverless'] }, () => {
beforeEach(() => {
login();
deleteValueLists([
KNOWN_VALUE_LIST_FILES.TEXT,
KNOWN_VALUE_LIST_FILES.IPs,
KNOWN_VALUE_LIST_FILES.CIDRs,
]);
createListsIndex();
visit(RULES_MANAGEMENT_URL);
waitForListsIndex();
waitForValueListsModalToBeLoaded();
});
it('can open and close the modal', () => {
openValueListsModal();
closeValueListsModal();
});
describe('create list types', () => {
beforeEach(() => {
login();
deleteValueLists([
KNOWN_VALUE_LIST_FILES.TEXT,
KNOWN_VALUE_LIST_FILES.IPs,
KNOWN_VALUE_LIST_FILES.CIDRs,
]);
createListsIndex();
visit(RULES_MANAGEMENT_URL);
waitForListsIndex();
waitForValueListsModalToBeLoaded();
});
it('can open and close the modal', () => {
openValueListsModal();
closeValueListsModal();
});
// TODO: https://github.com/elastic/kibana/issues/161539
// Flaky in serverless tests
describe('create list types', { tags: ['@brokenInServerless'] }, () => {
beforeEach(() => {
openValueListsModal();
});
it('creates a "keyword" list from an uploaded file', () => {
selectValueListType('keyword');
selectValueListsFile(KNOWN_VALUE_LIST_FILES.TEXT);
uploadValueList();
it('creates a "keyword" list from an uploaded file', () => {
selectValueListType('keyword');
selectValueListsFile(KNOWN_VALUE_LIST_FILES.TEXT);
uploadValueList();
cy.get(VALUE_LISTS_TABLE)
.find(VALUE_LISTS_ROW)
.should(($row) => {
expect($row.text()).to.contain(KNOWN_VALUE_LIST_FILES.TEXT);
expect($row.text()).to.contain('Keywords');
});
});
cy.get(VALUE_LISTS_TABLE)
.find(VALUE_LISTS_ROW)
.should(($row) => {
expect($row.text()).to.contain(KNOWN_VALUE_LIST_FILES.TEXT);
expect($row.text()).to.contain('Keywords');
});
});
it('creates a "text" list from an uploaded file', () => {
selectValueListType('text');
selectValueListsFile(KNOWN_VALUE_LIST_FILES.TEXT);
uploadValueList();
it('creates a "text" list from an uploaded file', () => {
selectValueListType('text');
selectValueListsFile(KNOWN_VALUE_LIST_FILES.TEXT);
uploadValueList();
cy.get(VALUE_LISTS_TABLE)
.find(VALUE_LISTS_ROW)
.should(($row) => {
expect($row.text()).to.contain(KNOWN_VALUE_LIST_FILES.TEXT);
expect($row.text()).to.contain('Text');
});
});
cy.get(VALUE_LISTS_TABLE)
.find(VALUE_LISTS_ROW)
.should(($row) => {
expect($row.text()).to.contain(KNOWN_VALUE_LIST_FILES.TEXT);
expect($row.text()).to.contain('Text');
});
});
it('creates a "ip" list from an uploaded file', () => {
selectValueListType('ip');
selectValueListsFile(KNOWN_VALUE_LIST_FILES.IPs);
uploadValueList();
it('creates a "ip" list from an uploaded file', () => {
selectValueListType('ip');
selectValueListsFile(KNOWN_VALUE_LIST_FILES.IPs);
uploadValueList();
cy.get(VALUE_LISTS_TABLE)
.find(VALUE_LISTS_ROW)
.should(($row) => {
expect($row.text()).to.contain(KNOWN_VALUE_LIST_FILES.IPs);
expect($row.text()).to.contain('IP addresses');
});
});
cy.get(VALUE_LISTS_TABLE)
.find(VALUE_LISTS_ROW)
.should(($row) => {
expect($row.text()).to.contain(KNOWN_VALUE_LIST_FILES.IPs);
expect($row.text()).to.contain('IP addresses');
});
});
it('creates a "ip_range" list from an uploaded file', () => {
selectValueListType('ip_range');
selectValueListsFile(KNOWN_VALUE_LIST_FILES.CIDRs);
uploadValueList();
it('creates a "ip_range" list from an uploaded file', () => {
selectValueListType('ip_range');
selectValueListsFile(KNOWN_VALUE_LIST_FILES.CIDRs);
uploadValueList();
cy.get(VALUE_LISTS_TABLE)
.find(VALUE_LISTS_ROW)
.should(($row) => {
expect($row.text()).to.contain(KNOWN_VALUE_LIST_FILES.CIDRs);
expect($row.text()).to.contain('IP ranges');
});
});
});
cy.get(VALUE_LISTS_TABLE)
.find(VALUE_LISTS_ROW)
.should(($row) => {
expect($row.text()).to.contain(KNOWN_VALUE_LIST_FILES.CIDRs);
expect($row.text()).to.contain('IP ranges');
});
describe('delete list types', () => {
it('deletes a "keyword" list from an uploaded file', () => {
importValueList(KNOWN_VALUE_LIST_FILES.TEXT, 'keyword');
openValueListsModal();
deleteValueListsFile(KNOWN_VALUE_LIST_FILES.TEXT);
cy.get(VALUE_LISTS_TABLE)
.find(VALUE_LISTS_ROW)
.should(($row) => {
expect($row.text()).not.to.contain(KNOWN_VALUE_LIST_FILES.TEXT);
});
});
it('deletes a "text" list from an uploaded file', () => {
importValueList(KNOWN_VALUE_LIST_FILES.TEXT, 'text');
openValueListsModal();
deleteValueListsFile(KNOWN_VALUE_LIST_FILES.TEXT);
cy.get(VALUE_LISTS_TABLE)
.find(VALUE_LISTS_ROW)
.should(($row) => {
expect($row.text()).not.to.contain(KNOWN_VALUE_LIST_FILES.TEXT);
});
});
it('deletes a "ip" from an uploaded file', () => {
importValueList(KNOWN_VALUE_LIST_FILES.IPs, 'ip');
openValueListsModal();
deleteValueListsFile(KNOWN_VALUE_LIST_FILES.IPs);
cy.get(VALUE_LISTS_TABLE)
.find(VALUE_LISTS_ROW)
.should(($row) => {
expect($row.text()).not.to.contain(KNOWN_VALUE_LIST_FILES.IPs);
});
});
it('deletes a "ip_range" from an uploaded file', () => {
importValueList(KNOWN_VALUE_LIST_FILES.CIDRs, 'ip_range', ['192.168.100.0']);
openValueListsModal();
deleteValueListsFile(KNOWN_VALUE_LIST_FILES.CIDRs);
cy.get(VALUE_LISTS_TABLE)
.find(VALUE_LISTS_ROW)
.should(($row) => {
expect($row.text()).not.to.contain(KNOWN_VALUE_LIST_FILES.CIDRs);
});
});
});
describe('export list types', () => {
it('exports a "keyword" list from an uploaded file', () => {
cy.intercept('POST', `/api/lists/items/_export?list_id=${KNOWN_VALUE_LIST_FILES.TEXT}`).as(
'exportList'
);
importValueList(KNOWN_VALUE_LIST_FILES.TEXT, 'keyword');
// Importing value lists includes bulk creation of list items with refresh=wait_for
// While it should wait for data update and return after that it's not always a case with bulk operations.
// Sometimes list items are empty making this test flaky.
// To fix it refresh used list items index (for the default space)
refreshIndex('.items-default');
openValueListsModal();
exportValueList();
cy.wait('@exportList').then(({ response }) => {
cy.fixture(KNOWN_VALUE_LIST_FILES.TEXT).then((list: string) => {
const [lineOne, lineTwo] = list.split('\n');
expect(response?.body).to.contain(lineOne);
expect(response?.body).to.contain(lineTwo);
});
});
});
// TODO: https://github.com/elastic/kibana/issues/161539
// Flaky in serverless tests
describe('delete list types', { tags: ['@brokenInServerless'] }, () => {
it('deletes a "keyword" list from an uploaded file', () => {
importValueList(KNOWN_VALUE_LIST_FILES.TEXT, 'keyword');
openValueListsModal();
deleteValueListsFile(KNOWN_VALUE_LIST_FILES.TEXT);
cy.get(VALUE_LISTS_TABLE)
.find(VALUE_LISTS_ROW)
.should(($row) => {
expect($row.text()).not.to.contain(KNOWN_VALUE_LIST_FILES.TEXT);
});
});
it('exports a "text" list from an uploaded file', () => {
cy.intercept('POST', `/api/lists/items/_export?list_id=${KNOWN_VALUE_LIST_FILES.TEXT}`).as(
'exportList'
);
importValueList(KNOWN_VALUE_LIST_FILES.TEXT, 'text');
it('deletes a "text" list from an uploaded file', () => {
importValueList(KNOWN_VALUE_LIST_FILES.TEXT, 'text');
openValueListsModal();
deleteValueListsFile(KNOWN_VALUE_LIST_FILES.TEXT);
cy.get(VALUE_LISTS_TABLE)
.find(VALUE_LISTS_ROW)
.should(($row) => {
expect($row.text()).not.to.contain(KNOWN_VALUE_LIST_FILES.TEXT);
});
});
// Importing value lists includes bulk creation of list items with refresh=wait_for
// While it should wait for data update and return after that it's not always a case with bulk operations.
// Sometimes list items are empty making this test flaky.
// To fix it refresh used list items index (for the default space)
refreshIndex('.items-default');
it('deletes a "ip" from an uploaded file', () => {
importValueList(KNOWN_VALUE_LIST_FILES.IPs, 'ip');
openValueListsModal();
deleteValueListsFile(KNOWN_VALUE_LIST_FILES.IPs);
cy.get(VALUE_LISTS_TABLE)
.find(VALUE_LISTS_ROW)
.should(($row) => {
expect($row.text()).not.to.contain(KNOWN_VALUE_LIST_FILES.IPs);
});
});
openValueListsModal();
exportValueList();
it('deletes a "ip_range" from an uploaded file', () => {
importValueList(KNOWN_VALUE_LIST_FILES.CIDRs, 'ip_range', ['192.168.100.0']);
openValueListsModal();
deleteValueListsFile(KNOWN_VALUE_LIST_FILES.CIDRs);
cy.get(VALUE_LISTS_TABLE)
.find(VALUE_LISTS_ROW)
.should(($row) => {
expect($row.text()).not.to.contain(KNOWN_VALUE_LIST_FILES.CIDRs);
});
cy.wait('@exportList').then(({ response }) => {
cy.fixture(KNOWN_VALUE_LIST_FILES.TEXT).then((list: string) => {
const [lineOne, lineTwo] = list.split('\n');
expect(response?.body).to.contain(lineOne);
expect(response?.body).to.contain(lineTwo);
});
});
});
// TODO: https://github.com/elastic/kibana/issues/161539
// Flaky in serverless tests
describe('export list types', { tags: ['@brokenInServerless'] }, () => {
it('exports a "keyword" list from an uploaded file', () => {
cy.intercept('POST', `/api/lists/items/_export?list_id=${KNOWN_VALUE_LIST_FILES.TEXT}`).as(
'exportList'
);
importValueList(KNOWN_VALUE_LIST_FILES.TEXT, 'keyword');
it('exports a "ip" list from an uploaded file', () => {
cy.intercept('POST', `/api/lists/items/_export?list_id=${KNOWN_VALUE_LIST_FILES.IPs}`).as(
'exportList'
);
importValueList(KNOWN_VALUE_LIST_FILES.IPs, 'ip');
// Importing value lists includes bulk creation of list items with refresh=wait_for
// While it should wait for data update and return after that it's not always a case with bulk operations.
// Sometimes list items are empty making this test flaky.
// To fix it refresh used list items index (for the default space)
refreshIndex('.items-default');
// Importing value lists includes bulk creation of list items with refresh=wait_for
// While it should wait for data update and return after that it's not always a case with bulk operations.
// Sometimes list items are empty making this test flaky.
// To fix it refresh used list items index (for the default space)
refreshIndex('.items-default');
openValueListsModal();
exportValueList();
cy.wait('@exportList').then(({ response }) => {
cy.fixture(KNOWN_VALUE_LIST_FILES.TEXT).then((list: string) => {
const [lineOne, lineTwo] = list.split('\n');
expect(response?.body).to.contain(lineOne);
expect(response?.body).to.contain(lineTwo);
});
openValueListsModal();
exportValueList();
cy.wait('@exportList').then(({ response }) => {
cy.fixture(KNOWN_VALUE_LIST_FILES.IPs).then((list: string) => {
const [lineOne, lineTwo] = list.split('\n');
expect(response?.body).to.contain(lineOne);
expect(response?.body).to.contain(lineTwo);
});
});
});
it('exports a "text" list from an uploaded file', () => {
cy.intercept('POST', `/api/lists/items/_export?list_id=${KNOWN_VALUE_LIST_FILES.TEXT}`).as(
'exportList'
);
importValueList(KNOWN_VALUE_LIST_FILES.TEXT, 'text');
it('exports a "ip_range" list from an uploaded file', () => {
cy.intercept('POST', `/api/lists/items/_export?list_id=${KNOWN_VALUE_LIST_FILES.CIDRs}`).as(
'exportList'
);
importValueList(KNOWN_VALUE_LIST_FILES.CIDRs, 'ip_range', ['192.168.100.0']);
// Importing value lists includes bulk creation of list items with refresh=wait_for
// While it should wait for data update and return after that it's not always a case with bulk operations.
// Sometimes list items are empty making this test flaky.
// To fix it refresh used list items index (for the default space)
refreshIndex('.items-default');
// Importing value lists includes bulk creation of list items with refresh=wait_for
// While it should wait for data update and return after that it's not always a case with bulk operations.
// Sometimes list items are empty making this test flaky.
// To fix it refresh used list items index (for the default space)
refreshIndex('.items-default');
openValueListsModal();
exportValueList();
cy.wait('@exportList').then(({ response }) => {
cy.fixture(KNOWN_VALUE_LIST_FILES.TEXT).then((list: string) => {
const [lineOne, lineTwo] = list.split('\n');
expect(response?.body).to.contain(lineOne);
expect(response?.body).to.contain(lineTwo);
});
});
});
it('exports a "ip" list from an uploaded file', () => {
cy.intercept('POST', `/api/lists/items/_export?list_id=${KNOWN_VALUE_LIST_FILES.IPs}`).as(
'exportList'
);
importValueList(KNOWN_VALUE_LIST_FILES.IPs, 'ip');
// Importing value lists includes bulk creation of list items with refresh=wait_for
// While it should wait for data update and return after that it's not always a case with bulk operations.
// Sometimes list items are empty making this test flaky.
// To fix it refresh used list items index (for the default space)
refreshIndex('.items-default');
openValueListsModal();
exportValueList();
cy.wait('@exportList').then(({ response }) => {
cy.fixture(KNOWN_VALUE_LIST_FILES.IPs).then((list: string) => {
const [lineOne, lineTwo] = list.split('\n');
expect(response?.body).to.contain(lineOne);
expect(response?.body).to.contain(lineTwo);
});
});
});
it('exports a "ip_range" list from an uploaded file', () => {
cy.intercept('POST', `/api/lists/items/_export?list_id=${KNOWN_VALUE_LIST_FILES.CIDRs}`).as(
'exportList'
);
importValueList(KNOWN_VALUE_LIST_FILES.CIDRs, 'ip_range', ['192.168.100.0']);
// Importing value lists includes bulk creation of list items with refresh=wait_for
// While it should wait for data update and return after that it's not always a case with bulk operations.
// Sometimes list items are empty making this test flaky.
// To fix it refresh used list items index (for the default space)
refreshIndex('.items-default');
openValueListsModal();
exportValueList();
cy.wait('@exportList').then(({ response }) => {
cy.fixture(KNOWN_VALUE_LIST_FILES.CIDRs).then((list: string) => {
const [lineOne] = list.split('\n');
expect(response?.body).to.contain(lineOne);
});
openValueListsModal();
exportValueList();
cy.wait('@exportList').then(({ response }) => {
cy.fixture(KNOWN_VALUE_LIST_FILES.CIDRs).then((list: string) => {
const [lineOne] = list.split('\n');
expect(response?.body).to.contain(lineOne);
});
});
});
});
// TODO: https://github.com/elastic/kibana/issues/164451 We should find a way to make this spec work in Serverless
// TODO: https://github.com/elastic/kibana/issues/161539
describe(
'user with restricted access role',
{ tags: ['@ess', '@serverless', '@skipInServerless'] },
() => {
it('Does not allow a t1 analyst user to upload a value list', () => {
login(ROLES.t1_analyst);
visit(RULES_MANAGEMENT_URL, { role: ROLES.t1_analyst });
cy.get(VALUE_LISTS_MODAL_ACTIVATOR).should('have.attr', 'disabled');
});
}
);
});

View file

@ -5,25 +5,25 @@
* 2.0.
*/
import { getNewRule } from '../../objects/rule';
import { ALERTS_COUNT } from '../../screens/alerts';
import { getNewRule } from '../../../objects/rule';
import { ALERTS_COUNT } from '../../../screens/alerts';
import {
clickAlertsHistogramLegend,
clickAlertsHistogramLegendAddToTimeline,
clickAlertsHistogramLegendFilterFor,
clickAlertsHistogramLegendFilterOut,
selectAlertsHistogram,
} from '../../tasks/alerts';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
import { login } from '../../tasks/login';
import { visitWithTimeRange } from '../../tasks/navigation';
import { ALERTS_URL } from '../../urls/navigation';
} from '../../../tasks/alerts';
import { createRule } from '../../../tasks/api_calls/rules';
import { cleanKibana } from '../../../tasks/common';
import { login } from '../../../tasks/login';
import { visitWithTimeRange } from '../../../tasks/navigation';
import { ALERTS_URL } from '../../../urls/navigation';
import {
GLOBAL_SEARCH_BAR_FILTER_ITEM,
GLOBAL_SEARCH_BAR_FILTER_ITEM_DELETE,
} from '../../screens/search_bar';
import { TOASTER } from '../../screens/alerts_detection_rules';
} from '../../../screens/search_bar';
import { TOASTER } from '../../../screens/alerts_detection_rules';
// TODO: https://github.com/elastic/kibana/issues/161539
describe(

View file

@ -0,0 +1,68 @@
/*
* 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 { waitForAlertsToPopulate } from '../../../tasks/create_new_rule';
import { login } from '../../../tasks/login';
import { visitWithTimeRange } from '../../../tasks/navigation';
import { ALERTS_URL, TIMELINES_URL } from '../../../urls/navigation';
import { ALERTS_HISTOGRAM_SERIES, ALERT_RULE_NAME, MESSAGE } from '../../../screens/alerts';
import { TIMELINE_QUERY, TIMELINE_VIEW_IN_ANALYZER } from '../../../screens/timeline';
import { selectAlertsHistogram } from '../../../tasks/alerts';
import { createTimeline } from '../../../tasks/timelines';
describe('Ransomware Detection Alerts', { tags: ['@ess', '@serverless'] }, () => {
before(() => {
cy.task('esArchiverLoad', {
archiveName: 'ransomware_detection',
useCreate: true,
docsOnly: true,
});
});
describe('Ransomware display in Alerts Section', () => {
beforeEach(() => {
login();
visitWithTimeRange(ALERTS_URL);
waitForAlertsToPopulate();
});
describe('Alerts table', () => {
it('shows Ransomware Alerts', () => {
cy.get(ALERT_RULE_NAME).should('have.text', 'Ransomware Detection Alert');
});
});
describe('Trend Chart', () => {
beforeEach(() => {
selectAlertsHistogram();
});
it('shows Ransomware Detection Alert in the trend chart', () => {
cy.get(ALERTS_HISTOGRAM_SERIES).should('have.text', 'Ransomware Detection Alert');
});
});
});
// FLAKY: https://github.com/elastic/kibana/issues/168602
describe.skip('Ransomware in Timelines', () => {
before(() => {
login();
visitWithTimeRange(TIMELINES_URL);
createTimeline();
});
it('Renders ransomware entries in timelines table', () => {
cy.get(TIMELINE_QUERY).type('event.code: "ransomware"{enter}');
// Wait for grid to load, it should have an analyzer icon
cy.get(TIMELINE_VIEW_IN_ANALYZER).should('exist');
cy.get(MESSAGE).should('have.text', 'Ransomware Detection Alert');
});
});
});

View file

@ -0,0 +1,74 @@
/*
* 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 { waitForAlertsToPopulate } from '../../../tasks/create_new_rule';
import { login } from '../../../tasks/login';
import { visitWithTimeRange } from '../../../tasks/navigation';
import { ALERTS_URL, TIMELINES_URL } from '../../../urls/navigation';
import { ALERTS_HISTOGRAM_SERIES, ALERT_RULE_NAME, MESSAGE } from '../../../screens/alerts';
import { TIMELINE_QUERY, TIMELINE_VIEW_IN_ANALYZER } from '../../../screens/timeline';
import { selectAlertsHistogram } from '../../../tasks/alerts';
import { createTimeline } from '../../../tasks/timelines';
import { cleanKibana } from '../../../tasks/common';
describe('Ransomware Prevention Alerts', { tags: ['@ess', '@serverless'] }, () => {
before(() => {
cleanKibana();
cy.task('esArchiverLoad', {
archiveName: 'ransomware_prevention',
useCreate: true,
docsOnly: true,
});
});
after(() => {
cy.task('esArchiverUnload', 'ransomware_prevention');
});
describe('Ransomware display in Alerts Section', () => {
beforeEach(() => {
login();
visitWithTimeRange(ALERTS_URL);
waitForAlertsToPopulate();
});
describe('Alerts table', () => {
it('shows Ransomware Alerts', () => {
cy.get(ALERT_RULE_NAME).should('have.text', 'Ransomware Prevention Alert');
});
});
describe('Trend Chart', () => {
beforeEach(() => {
selectAlertsHistogram();
});
it('shows Ransomware Prevention Alert in the trend chart', () => {
cy.get(ALERTS_HISTOGRAM_SERIES).should('have.text', 'Ransomware Prevention Alert');
});
});
});
describe('Ransomware in Timelines', () => {
beforeEach(() => {
login();
visitWithTimeRange(TIMELINES_URL);
createTimeline();
});
it('Renders ransomware entries in timelines table', () => {
cy.get(TIMELINE_QUERY).type('event.code: "ransomware"{enter}');
// Wait for grid to load, it should have an analyzer icon
cy.get(TIMELINE_VIEW_IN_ANALYZER).should('exist');
cy.get(MESSAGE).should('have.text', 'Ransomware Prevention Alert');
});
});
});

View file

@ -304,6 +304,12 @@ export const goToAcknowledgedAlerts = () => {
cy.get(TIMELINE_COLUMN_SPINNER).should('not.exist');
};
export const markAlertsAcknowledged = () => {
cy.get(TAKE_ACTION_POPOVER_BTN).click({ force: true });
cy.get(MARK_ALERT_ACKNOWLEDGED_BTN).should('be.visible');
cy.get(MARK_ALERT_ACKNOWLEDGED_BTN).click();
};
export const markAcknowledgedFirstAlert = () => {
expandFirstAlertActions();
cy.get(MARK_ALERT_ACKNOWLEDGED_BTN).should('be.visible');