Skip to content

Commit

Permalink
[SecuritySolution][Bug] Fix to add empty values to timeline (elastic#…
Browse files Browse the repository at this point in the history
…138510) (elastic#138556)

Fixes: elastic#118846

Issue: `Add to Timeline` action was not working for empty values.

https://user-images.githubusercontent.com/61860752/142180967-972a0438-154e-47c7-b058-be3abc4ac353.mp4

Solution:
Empty values can be added in timeline with condition of `NOT EXISTS` and hence on `Add to Timeline` click, data provider is modified to add `NOT EXISTS` condition to the timeline. Please see demo below:

https://user-images.githubusercontent.com/7485038/183914875-0b3c7e5f-7e12-40f2-a0c2-8b773434480b.mov

(cherry picked from commit 07d1ec8)

Co-authored-by: Jatin Kathuria <[email protected]>
  • Loading branch information
kibanamachine and logeekal authored Aug 10, 2022
1 parent ae08b6e commit a9ac8f1
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,23 @@
*/

import { getNewRule } from '../../objects/rule';
import { PROVIDER_BADGE } from '../../screens/timeline';
import {
ALERT_TABLE_FILE_NAME_HEADER,
ALERT_TABLE_FILE_NAME_VALUES,
ALERT_TABLE_SEVERITY_VALUES,
PROVIDER_BADGE,
} from '../../screens/timeline';

import { investigateFirstAlertInTimeline } from '../../tasks/alerts';
import {
addAlertPropertyToTimeline,
investigateFirstAlertInTimeline,
scrollAlertTableColumnIntoView,
} from '../../tasks/alerts';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
import { waitForAlertsToPopulate } from '../../tasks/create_new_rule';
import { login, visit } from '../../tasks/login';
import { openActiveTimeline } from '../../tasks/timeline';

import { ALERTS_URL } from '../../urls/navigation';

Expand All @@ -37,4 +47,24 @@ describe('Alerts timeline', () => {
cy.get(PROVIDER_BADGE).filter(':visible').should('have.text', eventId);
});
});

it('Add a non-empty property to default timeline', () => {
cy.get(ALERT_TABLE_SEVERITY_VALUES)
.first()
.invoke('text')
.then((severityVal) => {
addAlertPropertyToTimeline(ALERT_TABLE_SEVERITY_VALUES, 0);
openActiveTimeline();
cy.get(PROVIDER_BADGE)
.first()
.should('have.text', `kibana.alert.severity: "${severityVal}"`);
});
});

it('Add an empty property to default timeline', () => {
scrollAlertTableColumnIntoView(ALERT_TABLE_FILE_NAME_HEADER);
addAlertPropertyToTimeline(ALERT_TABLE_FILE_NAME_VALUES, 0);
openActiveTimeline();
cy.get(PROVIDER_BADGE).first().should('have.text', 'NOT file.name exists');
});
});
15 changes: 15 additions & 0 deletions x-pack/plugins/security_solution/cypress/screens/timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,3 +267,18 @@ export const USER_KPI = '[data-test-subj="siem-timeline-user-kpi"]';
export const EDIT_TIMELINE_BTN = '[data-test-subj="save-timeline-button-icon"]';

export const EDIT_TIMELINE_TOOLTIP = '[data-test-subj="save-timeline-btn-tooltip"]';

export const ALERT_TABLE_SEVERITY_VALUES =
'[data-test-subj="formatted-field-kibana.alert.severity"]';

export const ALERT_TABLE_FILE_NAME_HEADER = '[data-gridcell-column-id="file.name"]';

export const ALERT_TABLE_FILE_NAME_VALUES =
'[data-gridcell-column-id="file.name"][data-test-subj="dataGridRowCell"]'; // empty column for the test data

export const ALERT_TABLE_CELL_ACTIONS_ADD_TO_TIMELINE = '[data-test-subj="add-to-timeline"]';

export const ACTIVE_TIMELINE_BOTTOM_BAR =
'[data-test-subj="flyoutBottomBar"] .active-timeline-button';

export const DATA_GRID_BODY = '[data-test-subj=body-data-grid] .euiDataGrid__virtualized';
10 changes: 9 additions & 1 deletion x-pack/plugins/security_solution/cypress/tasks/alerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ import {
TIMELINE_CONTEXT_MENU_BTN,
} from '../screens/alerts';
import { REFRESH_BUTTON } from '../screens/security_header';
import { TIMELINE_COLUMN_SPINNER } from '../screens/timeline';
import {
ALERT_TABLE_CELL_ACTIONS_ADD_TO_TIMELINE,
TIMELINE_COLUMN_SPINNER,
} from '../screens/timeline';
import {
UPDATE_ENRICHMENT_RANGE_BUTTON,
ENRICHMENT_QUERY_END_INPUT,
Expand Down Expand Up @@ -152,6 +155,11 @@ export const investigateFirstAlertInTimeline = () => {
cy.get(SEND_ALERT_TO_TIMELINE_BTN).first().click({ force: true });
};

export const addAlertPropertyToTimeline = (propertySelector: string, rowIndex: number) => {
cy.get(propertySelector).eq(rowIndex).trigger('mouseover');
cy.get(ALERT_TABLE_CELL_ACTIONS_ADD_TO_TIMELINE).first().click({ force: true });
};

export const waitForAlerts = () => {
cy.get(REFRESH_BUTTON).should('not.have.attr', 'aria-label', 'Needs updating');
};
Expand Down
5 changes: 5 additions & 0 deletions x-pack/plugins/security_solution/cypress/tasks/timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import {
TIMELINE_TAB_CONTENT_EQL,
TIMESTAMP_HOVER_ACTION_OVERFLOW_BTN,
TIMELINE_DATA_PROVIDER_FIELD_INPUT,
ACTIVE_TIMELINE_BOTTOM_BAR,
} from '../screens/timeline';
import { REFRESH_BUTTON, TIMELINE } from '../screens/timelines';

Expand Down Expand Up @@ -296,6 +297,10 @@ export const openTimelineById = (timelineId: string): Cypress.Chainable<JQuery<H
return cy.get(TIMELINE_TITLE_BY_ID(timelineId)).should('be.visible').click({ force: true });
};

export const openActiveTimeline = () => {
cy.get(ACTIVE_TIMELINE_BOTTOM_BAR).click({ force: true });
};

export const pinFirstEvent = (): Cypress.Chainable<JQuery<HTMLElement>> => {
return cy.get(PIN_EVENT).first().click({ force: true });
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import type { TimelineNonEcsData } from '@kbn/timelines-plugin/common/search_str
import type { DataProvider } from '@kbn/timelines-plugin/common/types';
import { getPageRowIndex } from '@kbn/timelines-plugin/public';
import { useGetMappedNonEcsValue } from '../../../timelines/components/timeline/body/data_driven_columns';
import { IS_OPERATOR } from '../../../timelines/components/timeline/data_providers/data_provider';
import {
EXISTS_OPERATOR,
IS_OPERATOR,
} from '../../../timelines/components/timeline/data_providers/data_provider';
import { escapeDataProviderId } from '../../components/drag_and_drop/helpers';
import { EmptyComponent, useKibanaServices } from './helpers';

Expand Down Expand Up @@ -41,23 +44,39 @@ export const getAddToTimelineCellAction = ({
[timelines]
);

const dataProvider: DataProvider[] = useMemo(
() =>
value?.map((x) => ({
and: [],
enabled: true,
id: `${escapeDataProviderId(columnId)}-row-${rowIndex}-col-${columnId}-val-${x}`,
name: x,
excluded: false,
kqlQuery: '',
queryMatch: {
field: columnId,
value: x,
operator: IS_OPERATOR,
const dataProvider: DataProvider[] = useMemo(() => {
const queryIdPrefix = `${escapeDataProviderId(columnId)}-row-${rowIndex}-col-${columnId}`;
if (!value) {
return [
{
and: [],
enabled: true,
kqlQuery: '',
id: `${queryIdPrefix}`,
name: '',
excluded: true,
queryMatch: {
field: columnId,
value: '',
operator: EXISTS_OPERATOR,
},
},
})) ?? [],
[columnId, rowIndex, value]
);
];
}
return value.map((x) => ({
and: [],
enabled: true,
excluded: false,
kqlQuery: '',
id: `${queryIdPrefix}-val-${x}`,
name: x,
queryMatch: {
field: columnId,
value: x,
operator: IS_OPERATOR,
},
}));
}, [columnId, rowIndex, value]);
const addToTimelineProps = useMemo(() => {
return {
Component,
Expand Down

0 comments on commit a9ac8f1

Please sign in to comment.