[Security Solution][Detections] Fixes bulk alert status update to only update alerts within the defined daterange (#82071)
## Summary Addresses https://github.com/elastic/kibana/issues/82004 where updating an alerts status would result in all alerts being updated as the request is sent without the necessary daterange filter. ##### Before: <details><summary>Query</summary> <p> ``` json { "conflicts": "proceed", "status": "open", "query": { "bool": { "must": [], "filter": [ { "match_all": {} }, { "term": { "signal.status": "closed" } } ], "should": [], "must_not": [ { "exists": { "field": "signal.rule.building_block_type" } } ] } } } ``` </p> </details> <p align="center"> <img width="500" src="https://user-images.githubusercontent.com/2946766/97628470-5db73580-19f2-11eb-8e51-61e428e7804f.gif" /> </p> ##### After: <details><summary>Query</summary> <p> ``` json { "conflicts": "proceed", "status": "closed", "query": { "bool": { "must": [], "filter": [ { "bool": { "filter": [ { "bool": { "should": [ { "range": { "@timestamp": { "gte": "2020-10-29T20:17:23.357Z" } } } ], "minimum_should_match": 1 } }, { "bool": { "should": [ { "range": { "@timestamp": { "lte": "2020-10-29T20:17:40.097Z" } } } ], "minimum_should_match": 1 } } ] } }, { "term": { "signal.status": "open" } } ], "should": [], "must_not": [ { "exists": { "field": "signal.rule.building_block_type" } } ] } } } ``` </p> </details> <p align="center"> <img width="500" src="https://user-images.githubusercontent.com/2946766/97628955-0796c200-19f3-11eb-9ec3-2a6ace17160b.gif" /> </p> ### Checklist Delete any items that are not applicable to this PR. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
This commit is contained in:
parent
4323357ef8
commit
74463a42f1
9 changed files with 79 additions and 56 deletions
|
@ -177,8 +177,6 @@ const EventsViewerComponent: React.FC<Props> = ({
|
|||
filters,
|
||||
kqlQuery: query,
|
||||
kqlMode,
|
||||
start,
|
||||
end,
|
||||
isEventViewer: true,
|
||||
});
|
||||
|
||||
|
|
|
@ -117,7 +117,6 @@ const StatefulTopNComponent: React.FC<Props> = ({
|
|||
browserFields,
|
||||
config: esQuery.getEsQueryConfig(kibana.services.uiSettings),
|
||||
dataProviders,
|
||||
end: activeTimelineTo,
|
||||
filters: activeTimelineFilters,
|
||||
indexPattern,
|
||||
kqlMode,
|
||||
|
@ -125,7 +124,6 @@ const StatefulTopNComponent: React.FC<Props> = ({
|
|||
language: 'kuery',
|
||||
query: activeTimelineKqlQueryExpression ?? '',
|
||||
},
|
||||
start: activeTimelineFrom,
|
||||
})?.filterQuery
|
||||
: undefined
|
||||
}
|
||||
|
|
|
@ -195,6 +195,8 @@ const AlertsUtilityBarComponent: React.FC<AlertsUtilityBarProps> = ({
|
|||
</UtilityBarAction>
|
||||
|
||||
<UtilityBarAction
|
||||
aria-label="selectAllAlerts"
|
||||
dataTestSubj="selectAllAlertsButton"
|
||||
iconType={showClearSelection ? 'cross' : 'pagesSelect'}
|
||||
onClick={() => {
|
||||
if (!showClearSelection) {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { TimelineType } from '../../../../common/types/timeline';
|
||||
import { Filter } from '../../../../../../../src/plugins/data/public';
|
||||
import { esFilters, Filter } from '../../../../../../../src/plugins/data/public';
|
||||
import {
|
||||
DataProvider,
|
||||
DataProviderType,
|
||||
|
@ -17,6 +17,7 @@ import {
|
|||
replaceTemplateFieldFromQuery,
|
||||
replaceTemplateFieldFromMatchFilters,
|
||||
reformatDataProviderWithNewValue,
|
||||
buildTimeRangeFilter,
|
||||
} from './helpers';
|
||||
import { mockTimelineDetails } from '../../../common/mock';
|
||||
|
||||
|
@ -530,4 +531,38 @@ describe('helpers', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildTimeRangeFilter', () => {
|
||||
test('time range filter is created with from and to', () => {
|
||||
const from = '2020-10-29T21:06:10.192Z';
|
||||
const to = '2020-10-29T21:07:38.774Z';
|
||||
const timeRangeFilter = buildTimeRangeFilter(from, to);
|
||||
expect(timeRangeFilter).toEqual([
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: '2020-10-29T21:06:10.192Z',
|
||||
lt: '2020-10-29T21:07:38.774Z',
|
||||
format: 'strict_date_optional_time',
|
||||
},
|
||||
},
|
||||
meta: {
|
||||
type: 'range',
|
||||
disabled: false,
|
||||
negate: false,
|
||||
alias: null,
|
||||
key: '@timestamp',
|
||||
params: {
|
||||
gte: from,
|
||||
lt: to,
|
||||
format: 'strict_date_optional_time',
|
||||
},
|
||||
},
|
||||
$state: {
|
||||
store: esFilters.FilterStateStore.APP_STATE,
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,7 +5,12 @@
|
|||
*/
|
||||
|
||||
import { isEmpty } from 'lodash/fp';
|
||||
import { Filter, esKuery, KueryNode } from '../../../../../../../src/plugins/data/public';
|
||||
import {
|
||||
Filter,
|
||||
esKuery,
|
||||
KueryNode,
|
||||
esFilters,
|
||||
} from '../../../../../../../src/plugins/data/public';
|
||||
import {
|
||||
DataProvider,
|
||||
DataProviderType,
|
||||
|
@ -214,3 +219,30 @@ export const replaceTemplateFieldFromDataProviders = (
|
|||
}
|
||||
return newDataProvider;
|
||||
});
|
||||
|
||||
export const buildTimeRangeFilter = (from: string, to: string): Filter[] => [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: from,
|
||||
lt: to,
|
||||
format: 'strict_date_optional_time',
|
||||
},
|
||||
},
|
||||
meta: {
|
||||
type: 'range',
|
||||
disabled: false,
|
||||
negate: false,
|
||||
alias: null,
|
||||
key: '@timestamp',
|
||||
params: {
|
||||
gte: from,
|
||||
lt: to,
|
||||
format: 'strict_date_optional_time',
|
||||
},
|
||||
},
|
||||
$state: {
|
||||
store: esFilters.FilterStateStore.APP_STATE,
|
||||
},
|
||||
} as Filter,
|
||||
];
|
||||
|
|
|
@ -46,6 +46,7 @@ import {
|
|||
} from '../../../common/components/toasters';
|
||||
import { SourcererScopeName } from '../../../common/store/sourcerer/model';
|
||||
import { useSourcererScope } from '../../../common/containers/sourcerer';
|
||||
import { buildTimeRangeFilter } from './helpers';
|
||||
|
||||
interface OwnProps {
|
||||
timelineId: TimelineIdLiteral;
|
||||
|
@ -105,13 +106,14 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
|
|||
dataProviders: [],
|
||||
indexPattern: indexPatterns,
|
||||
browserFields,
|
||||
filters: isEmpty(defaultFilters)
|
||||
? [...globalFilters, ...customFilters]
|
||||
: [...(defaultFilters ?? []), ...globalFilters, ...customFilters],
|
||||
filters: [
|
||||
...(defaultFilters ?? []),
|
||||
...globalFilters,
|
||||
...customFilters,
|
||||
...buildTimeRangeFilter(from, to),
|
||||
],
|
||||
kqlQuery: globalQuery,
|
||||
kqlMode: globalQuery.language,
|
||||
start: from,
|
||||
end: to,
|
||||
isEventViewer: true,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -14,8 +14,6 @@ import { mockBrowserFields } from '../../../common/containers/source/mock';
|
|||
import { EsQueryConfig, Filter, esFilters } from '../../../../../../../src/plugins/data/public';
|
||||
|
||||
const cleanUpKqlQuery = (str: string) => str.replace(/\n/g, '').replace(/\s\s+/g, ' ');
|
||||
const startDate = '2018-03-23T18:49:23.132Z';
|
||||
const endDate = '2018-03-24T03:33:52.253Z';
|
||||
|
||||
describe('Build KQL Query', () => {
|
||||
test('Build KQL query with one data provider', () => {
|
||||
|
@ -238,8 +236,6 @@ describe('Combined Queries', () => {
|
|||
filters: [],
|
||||
kqlQuery: { query: '', language: 'kuery' },
|
||||
kqlMode: 'search',
|
||||
start: startDate,
|
||||
end: endDate,
|
||||
})
|
||||
).toBeNull();
|
||||
});
|
||||
|
@ -255,8 +251,6 @@ describe('Combined Queries', () => {
|
|||
filters: [],
|
||||
kqlQuery: { query: '', language: 'kuery' },
|
||||
kqlMode: 'search',
|
||||
start: startDate,
|
||||
end: endDate,
|
||||
isEventViewer,
|
||||
})
|
||||
).toEqual({
|
||||
|
@ -300,8 +294,6 @@ describe('Combined Queries', () => {
|
|||
],
|
||||
kqlQuery: { query: '', language: 'kuery' },
|
||||
kqlMode: 'search',
|
||||
start: startDate,
|
||||
end: endDate,
|
||||
isEventViewer,
|
||||
})
|
||||
).toEqual({
|
||||
|
@ -320,8 +312,6 @@ describe('Combined Queries', () => {
|
|||
filters: [],
|
||||
kqlQuery: { query: '', language: 'kuery' },
|
||||
kqlMode: 'search',
|
||||
start: startDate,
|
||||
end: endDate,
|
||||
})!;
|
||||
expect(filterQuery).toEqual(
|
||||
'{"bool":{"must":[],"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 1"}}],"minimum_should_match":1}}],"should":[],"must_not":[]}}'
|
||||
|
@ -340,8 +330,6 @@ describe('Combined Queries', () => {
|
|||
filters: [],
|
||||
kqlQuery: { query: '', language: 'kuery' },
|
||||
kqlMode: 'search',
|
||||
start: startDate,
|
||||
end: endDate,
|
||||
})!;
|
||||
expect(filterQuery).toEqual(
|
||||
'{"bool":{"must":[],"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521848183232,"lte":1521848183232}}}],"minimum_should_match":1}}],"should":[],"must_not":[]}}'
|
||||
|
@ -360,8 +348,6 @@ describe('Combined Queries', () => {
|
|||
filters: [],
|
||||
kqlQuery: { query: '', language: 'kuery' },
|
||||
kqlMode: 'search',
|
||||
start: startDate,
|
||||
end: endDate,
|
||||
})!;
|
||||
expect(filterQuery).toEqual(
|
||||
'{"bool":{"must":[],"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521848183232,"lte":1521848183232}}}],"minimum_should_match":1}}],"should":[],"must_not":[]}}'
|
||||
|
@ -380,8 +366,6 @@ describe('Combined Queries', () => {
|
|||
filters: [],
|
||||
kqlQuery: { query: '', language: 'kuery' },
|
||||
kqlMode: 'search',
|
||||
start: startDate,
|
||||
end: endDate,
|
||||
})!;
|
||||
expect(filterQuery).toEqual(
|
||||
'{"bool":{"must":[],"filter":[{"bool":{"should":[{"match":{"event.end":1521848183232}}],"minimum_should_match":1}}],"should":[],"must_not":[]}}'
|
||||
|
@ -400,8 +384,6 @@ describe('Combined Queries', () => {
|
|||
filters: [],
|
||||
kqlQuery: { query: '', language: 'kuery' },
|
||||
kqlMode: 'search',
|
||||
start: startDate,
|
||||
end: endDate,
|
||||
})!;
|
||||
expect(filterQuery).toEqual(
|
||||
'{"bool":{"must":[],"filter":[{"bool":{"should":[{"match":{"event.end":1521848183232}}],"minimum_should_match":1}}],"should":[],"must_not":[]}}'
|
||||
|
@ -417,8 +399,6 @@ describe('Combined Queries', () => {
|
|||
filters: [],
|
||||
kqlQuery: { query: 'host.name: "host-1"', language: 'kuery' },
|
||||
kqlMode: 'search',
|
||||
start: startDate,
|
||||
end: endDate,
|
||||
})!;
|
||||
expect(filterQuery).toEqual(
|
||||
'{"bool":{"must":[],"filter":[{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}}],"should":[],"must_not":[]}}'
|
||||
|
@ -435,8 +415,6 @@ describe('Combined Queries', () => {
|
|||
filters: [],
|
||||
kqlQuery: { query: 'host.name: "host-1"', language: 'kuery' },
|
||||
kqlMode: 'search',
|
||||
start: startDate,
|
||||
end: endDate,
|
||||
})!;
|
||||
expect(filterQuery).toEqual(
|
||||
'{"bool":{"must":[],"filter":[{"bool":{"should":[{"bool":{"should":[{"match_phrase":{"name":"Provider 1"}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}}],"minimum_should_match":1}}],"should":[],"must_not":[]}}'
|
||||
|
@ -453,8 +431,6 @@ describe('Combined Queries', () => {
|
|||
filters: [],
|
||||
kqlQuery: { query: 'host.name: "host-1"', language: 'kuery' },
|
||||
kqlMode: 'filter',
|
||||
start: startDate,
|
||||
end: endDate,
|
||||
})!;
|
||||
expect(filterQuery).toEqual(
|
||||
'{"bool":{"must":[],"filter":[{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 1"}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}}]}}],"should":[],"must_not":[]}}'
|
||||
|
@ -473,8 +449,6 @@ describe('Combined Queries', () => {
|
|||
filters: [],
|
||||
kqlQuery: { query: 'host.name: "host-1"', language: 'kuery' },
|
||||
kqlMode: 'search',
|
||||
start: startDate,
|
||||
end: endDate,
|
||||
})!;
|
||||
expect(filterQuery).toEqual(
|
||||
'{"bool":{"must":[],"filter":[{"bool":{"should":[{"bool":{"should":[{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 1"}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 3"}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"name":"Provider 4"}}],"minimum_should_match":1}}]}}]}},{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 2"}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"name":"Provider 5"}}],"minimum_should_match":1}}]}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}}],"minimum_should_match":1}}],"should":[],"must_not":[]}}'
|
||||
|
@ -493,8 +467,6 @@ describe('Combined Queries', () => {
|
|||
filters: [],
|
||||
kqlQuery: { query: 'host.name: "host-1"', language: 'kuery' },
|
||||
kqlMode: 'filter',
|
||||
start: startDate,
|
||||
end: endDate,
|
||||
})!;
|
||||
expect(filterQuery).toEqual(
|
||||
'{"bool":{"must":[],"filter":[{"bool":{"filter":[{"bool":{"should":[{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 1"}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 3"}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"name":"Provider 4"}}],"minimum_should_match":1}}]}}]}},{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 2"}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"name":"Provider 5"}}],"minimum_should_match":1}}]}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}}]}}],"should":[],"must_not":[]}}'
|
||||
|
|
|
@ -104,8 +104,6 @@ export const combineQueries = ({
|
|||
filters = [],
|
||||
kqlQuery,
|
||||
kqlMode,
|
||||
start,
|
||||
end,
|
||||
isEventViewer,
|
||||
}: {
|
||||
config: EsQueryConfig;
|
||||
|
@ -115,8 +113,6 @@ export const combineQueries = ({
|
|||
filters: Filter[];
|
||||
kqlQuery: Query;
|
||||
kqlMode: string;
|
||||
start: string;
|
||||
end: string;
|
||||
isEventViewer?: boolean;
|
||||
}): { filterQuery: string } | null => {
|
||||
const kuery: Query = { query: '', language: kqlQuery.language };
|
||||
|
|
|
@ -164,20 +164,8 @@ export const TimelineComponent: React.FC<Props> = ({
|
|||
filters,
|
||||
kqlQuery,
|
||||
kqlMode,
|
||||
start,
|
||||
end,
|
||||
}),
|
||||
[
|
||||
browserFields,
|
||||
dataProviders,
|
||||
esQueryConfig,
|
||||
start,
|
||||
end,
|
||||
filters,
|
||||
indexPattern,
|
||||
kqlMode,
|
||||
kqlQuery,
|
||||
]
|
||||
[browserFields, dataProviders, esQueryConfig, filters, indexPattern, kqlMode, kqlQuery]
|
||||
);
|
||||
|
||||
const canQueryTimeline = useMemo(
|
||||
|
|
Loading…
Reference in a new issue