[Security Solution] Sourcerer fix bug 121380 (#121581)

This commit is contained in:
Steph Milovic 2021-12-20 10:44:04 -07:00 committed by GitHub
parent 2150e74224
commit 78d15cd7f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 153 additions and 84 deletions

View file

@ -36,6 +36,9 @@ import { createUsersAndRoles, secReadCasesAll, secReadCasesAllUser } from '../..
import { TOASTER } from '../../screens/configure_cases';
import { DEFAULT_ALERTS_INDEX, DEFAULT_INDEX_PATTERN } from '../../../common/constants';
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 usersToCreate = [secReadCasesAllUser];
const rolesToCreate = [secReadCasesAll];
@ -129,103 +132,132 @@ describe('Sourcerer', () => {
isSourcererSelection('beats*');
});
});
});
describe('Timeline scope', () => {
beforeEach(() => {
cy.clearLocalStorage();
loginAndWaitForPage(TIMELINES_URL);
});
describe('Timeline scope', () => {
beforeEach(() => {
cy.clearLocalStorage();
loginAndWaitForPage(TIMELINES_URL);
it('correctly loads SIEM data view before and after signals index exists', () => {
openTimelineUsingToggle();
openSourcerer('timeline');
isDataViewSelection(siemDataViewTitle);
openAdvancedSettings();
isSourcererSelection(`auditbeat-*`);
isNotSourcererSelection(`${DEFAULT_ALERTS_INDEX}-default`);
isSourcererOptions(
[...DEFAULT_INDEX_PATTERN, `${DEFAULT_ALERTS_INDEX}-default`].filter(
(pattern) => pattern !== 'auditbeat-*'
)
);
waitForAlertsIndexToExist();
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('correctly loads SIEM data view before and after signals index exists', () => {
it('shows modified badge when index patterns change and removes when reset', () => {
openTimelineUsingToggle();
openSourcerer('timeline');
isDataViewSelection(siemDataViewTitle);
openDataViewSelection();
cy.get(SOURCERER.selectListOption).contains(dataViews[1]).click();
isDataViewSelection(dataViews[1]);
openAdvancedSettings();
isSourcererSelection(`auditbeat-*`);
isNotSourcererSelection(`${DEFAULT_ALERTS_INDEX}-default`);
isSourcererOptions(
[...DEFAULT_INDEX_PATTERN, `${DEFAULT_ALERTS_INDEX}-default`].filter(
(pattern) => pattern !== 'auditbeat-*'
)
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(() => {
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(() => {
loginAndWaitForPage(TIMELINES_URL);
waitForAlertsIndexToExist();
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();
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');
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', () => {
beforeEach(() => {
waitForAlertsIndexToExist();
});
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-*')
.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`);
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-*')
.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

@ -12,6 +12,8 @@ export interface Timeline {
description: string;
query: string;
id?: string;
dataViewId?: string;
indexNames?: string[];
}
export interface CompleteTimeline extends Timeline {
@ -46,6 +48,12 @@ export const getIndicatorMatchTimelineTemplate = (): CompleteTimeline => ({
templateTimelineId: '495ad7a7-316e-4544-8a0f-9c098daee76e',
});
export const getTimelineModifiedSourcerer = () => ({
...getTimeline(),
title: 'Auditbeat Timeline',
dataViewId: 'security-solution-default',
indexNames: ['auditbeat-*'],
});
/**
* Timeline query that finds no valid data to cut down on test failures
* or other issues for when we want to test one specific thing and not also

View file

@ -46,6 +46,9 @@ export const createTimeline = (timeline: CompleteTimeline) =>
description: timeline.description,
title: timeline.title,
savedQueryId: null,
...(timeline.dataViewId != null && timeline.indexNames != null
? { dataViewId: timeline.dataViewId, indexNames: timeline.indexNames }
: {}),
},
},
headers: { 'kbn-xsrf': 'cypress-creds' },

View file

@ -66,8 +66,20 @@ export const Sourcerer = React.memo<SourcererComponentProps>(({ scope: scopeId }
isTimelineSourcerer && selectedPatterns.join() === signalIndexName
);
const onUpdateDetectionAlertsChecked = useCallback(() => {
setIsOnlyDetectionAlertsChecked(
isTimelineSourcerer && selectedPatterns.join() === signalIndexName
);
}, [isTimelineSourcerer, selectedPatterns, signalIndexName]);
useEffect(() => {
onUpdateDetectionAlertsChecked();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedPatterns]);
const isOnlyDetectionAlerts: boolean =
isDetectionsSourcerer || (isTimelineSourcerer && isOnlyDetectionAlertsChecked);
const [isPopoverOpen, setPopoverIsOpen] = useState(false);
const [dataViewId, setDataViewId] = useState<string | null>(selectedDataViewId);
@ -75,6 +87,7 @@ export const Sourcerer = React.memo<SourcererComponentProps>(({ scope: scopeId }
allOptions,
dataViewSelectOptions,
isModified,
handleOutsideClick,
onChangeCombo,
renderOption,
selectedOptions,
@ -211,7 +224,14 @@ export const Sourcerer = React.memo<SourcererComponentProps>(({ scope: scopeId }
const onOutsideClick = useCallback(() => {
setDataViewId(selectedDataViewId);
setMissingPatterns(sourcererMissingPatterns);
}, [selectedDataViewId, sourcererMissingPatterns]);
onUpdateDetectionAlertsChecked();
handleOutsideClick();
}, [
handleOutsideClick,
onUpdateDetectionAlertsChecked,
selectedDataViewId,
sourcererMissingPatterns,
]);
const onExpandAdvancedOptionsClicked = useCallback(() => {
setExpandAdvancedOptions((prevState) => !prevState);

View file

@ -29,6 +29,7 @@ export type ModifiedTypes = 'modified' | 'alerts' | 'deprecated' | 'missingPatte
interface UsePickIndexPatterns {
allOptions: Array<EuiComboBoxOptionOption<string>>;
dataViewSelectOptions: Array<EuiSuperSelectOption<string>>;
handleOutsideClick: () => void;
isModified: ModifiedTypes;
onChangeCombo: (newSelectedDataViewId: Array<EuiComboBoxOptionOption<string>>) => void;
renderOption: ({ value }: EuiComboBoxOptionOption<string>) => React.ReactElement;
@ -161,7 +162,7 @@ export const usePickIndexPatterns = ({
selectedDataViewId
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedDataViewId, missingPatterns, scopeId, selectedPatterns]);
}, [isOnlyDetectionAlerts, selectedDataViewId, missingPatterns, scopeId, selectedPatterns]);
const onChangeCombo = useCallback((newSelectedOptions) => {
setSelectedOptions(newSelectedOptions);
@ -190,9 +191,14 @@ export const usePickIndexPatterns = ({
[dataViewId, defaultDataViewId, isModified, isOnlyDetectionAlerts, kibanaDataViews]
);
const handleOutsideClick = useCallback(() => {
setSelectedOptions(patternListToOptions(selectedPatterns));
}, [selectedPatterns]);
return {
allOptions,
dataViewSelectOptions,
handleOutsideClick,
isModified,
onChangeCombo,
renderOption,