mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
* Add runtime field support to query signals route
* Fix tests
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
(cherry picked from commit 3a59ba4b21
)
Co-authored-by: Marshall Main <55718608+marshallmain@users.noreply.github.com>
This commit is contained in:
parent
18d3e54bcf
commit
f489182683
10 changed files with 93 additions and 16 deletions
|
@ -15,6 +15,7 @@ export const querySignalsSchema = t.exact(
|
|||
size: PositiveInteger,
|
||||
track_total_hits: t.boolean,
|
||||
_source: t.array(t.string),
|
||||
runtime_mappings: t.unknown,
|
||||
})
|
||||
);
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../../../common/constants';
|
||||
import type { AlertsStackByField } from '../common/types';
|
||||
|
||||
|
@ -14,7 +15,8 @@ export const getAlertsCountQuery = (
|
|||
to: string,
|
||||
additionalFilters: Array<{
|
||||
bool: { filter: unknown[]; should: unknown[]; must_not: unknown[]; must: unknown[] };
|
||||
}> = []
|
||||
}> = [],
|
||||
runtimeMappings?: MappingRuntimeFields
|
||||
) => {
|
||||
return {
|
||||
size: 0,
|
||||
|
@ -44,5 +46,6 @@ export const getAlertsCountQuery = (
|
|||
],
|
||||
},
|
||||
},
|
||||
runtime_mappings: runtimeMappings,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types';
|
||||
import React, { memo, useMemo, useState, useEffect } from 'react';
|
||||
import uuid from 'uuid';
|
||||
|
||||
|
@ -31,10 +32,11 @@ interface AlertsCountPanelProps {
|
|||
filters?: Filter[];
|
||||
query?: Query;
|
||||
signalIndexName: string | null;
|
||||
runtimeMappings?: MappingRuntimeFields;
|
||||
}
|
||||
|
||||
export const AlertsCountPanel = memo<AlertsCountPanelProps>(
|
||||
({ filters, query, signalIndexName }) => {
|
||||
({ filters, query, signalIndexName, runtimeMappings }) => {
|
||||
const { to, from, deleteQuery, setQuery } = useGlobalTime();
|
||||
|
||||
// create a unique, but stable (across re-renders) query id
|
||||
|
@ -72,13 +74,21 @@ export const AlertsCountPanel = memo<AlertsCountPanelProps>(
|
|||
request,
|
||||
refetch,
|
||||
} = useQueryAlerts<{}, AlertsCountAggregation>({
|
||||
query: getAlertsCountQuery(selectedStackByOption, from, to, additionalFilters),
|
||||
query: getAlertsCountQuery(
|
||||
selectedStackByOption,
|
||||
from,
|
||||
to,
|
||||
additionalFilters,
|
||||
runtimeMappings
|
||||
),
|
||||
indexName: signalIndexName,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setAlertsQuery(getAlertsCountQuery(selectedStackByOption, from, to, additionalFilters));
|
||||
}, [setAlertsQuery, selectedStackByOption, from, to, additionalFilters]);
|
||||
setAlertsQuery(
|
||||
getAlertsCountQuery(selectedStackByOption, from, to, additionalFilters, runtimeMappings)
|
||||
);
|
||||
}, [setAlertsQuery, selectedStackByOption, from, to, additionalFilters, runtimeMappings]);
|
||||
|
||||
useInspectButton({
|
||||
setQuery,
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import { isEmpty } from 'lodash/fp';
|
||||
import moment from 'moment';
|
||||
|
||||
|
@ -38,7 +39,8 @@ export const getAlertsHistogramQuery = (
|
|||
to: string,
|
||||
additionalFilters: Array<{
|
||||
bool: { filter: unknown[]; should: unknown[]; must_not: unknown[]; must: unknown[] };
|
||||
}>
|
||||
}>,
|
||||
runtimeMappings?: MappingRuntimeFields
|
||||
) => {
|
||||
return {
|
||||
aggs: {
|
||||
|
@ -80,6 +82,7 @@ export const getAlertsHistogramQuery = (
|
|||
],
|
||||
},
|
||||
},
|
||||
runtime_mappings: runtimeMappings,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -184,6 +184,7 @@ describe('AlertsHistogramPanel', () => {
|
|||
},
|
||||
},
|
||||
],
|
||||
undefined,
|
||||
]);
|
||||
});
|
||||
wrapper.unmount();
|
||||
|
@ -237,6 +238,7 @@ describe('AlertsHistogramPanel', () => {
|
|||
},
|
||||
},
|
||||
],
|
||||
undefined,
|
||||
]);
|
||||
});
|
||||
wrapper.unmount();
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types';
|
||||
import type { Position } from '@elastic/charts';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiTitleSize } from '@elastic/eui';
|
||||
import numeral from '@elastic/numeral';
|
||||
|
@ -76,6 +77,7 @@ interface AlertsHistogramPanelProps {
|
|||
timelineId?: string;
|
||||
title?: string;
|
||||
updateDateRange: UpdateDateRange;
|
||||
runtimeMappings?: MappingRuntimeFields;
|
||||
}
|
||||
|
||||
const NO_LEGEND_DATA: LegendItem[] = [];
|
||||
|
@ -100,6 +102,7 @@ export const AlertsHistogramPanel = memo<AlertsHistogramPanelProps>(
|
|||
title = i18n.HISTOGRAM_HEADER,
|
||||
updateDateRange,
|
||||
titleSize = 'm',
|
||||
runtimeMappings,
|
||||
}) => {
|
||||
const { to, from, deleteQuery, setQuery } = useGlobalTime(false);
|
||||
|
||||
|
@ -125,7 +128,8 @@ export const AlertsHistogramPanel = memo<AlertsHistogramPanelProps>(
|
|||
selectedStackByOption,
|
||||
from,
|
||||
to,
|
||||
buildCombinedQueries(combinedQueries)
|
||||
buildCombinedQueries(combinedQueries),
|
||||
runtimeMappings
|
||||
),
|
||||
indexName: signalIndexName,
|
||||
});
|
||||
|
@ -231,15 +235,18 @@ export const AlertsHistogramPanel = memo<AlertsHistogramPanelProps>(
|
|||
selectedStackByOption,
|
||||
from,
|
||||
to,
|
||||
!isEmpty(converted) ? [converted] : []
|
||||
!isEmpty(converted) ? [converted] : [],
|
||||
runtimeMappings
|
||||
)
|
||||
);
|
||||
} catch (e) {
|
||||
setIsInspectDisabled(true);
|
||||
setAlertsQuery(getAlertsHistogramQuery(selectedStackByOption, from, to, []));
|
||||
setAlertsQuery(
|
||||
getAlertsHistogramQuery(selectedStackByOption, from, to, [], runtimeMappings)
|
||||
);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [selectedStackByOption, from, to, query, filters, combinedQueries]);
|
||||
}, [selectedStackByOption, from, to, query, filters, combinedQueries, runtimeMappings]);
|
||||
|
||||
const linkButton = useMemo(() => {
|
||||
if (showLinkToAlerts) {
|
||||
|
|
|
@ -130,10 +130,17 @@ const DetectionEnginePageComponent: React.FC<DetectionEngineComponentProps> = ({
|
|||
] = useUserData();
|
||||
const { loading: listsConfigLoading, needsConfiguration: needsListsConfiguration } =
|
||||
useListsConfig();
|
||||
|
||||
const {
|
||||
indexPattern,
|
||||
runtimeMappings,
|
||||
loading: isLoadingIndexPattern,
|
||||
} = useSourcererDataView(SourcererScopeName.detections);
|
||||
|
||||
const { formatUrl } = useFormatUrl(SecurityPageName.rules);
|
||||
const [showBuildingBlockAlerts, setShowBuildingBlockAlerts] = useState(false);
|
||||
const [showOnlyThreatIndicatorAlerts, setShowOnlyThreatIndicatorAlerts] = useState(false);
|
||||
const loading = userInfoLoading || listsConfigLoading;
|
||||
const loading = userInfoLoading || listsConfigLoading || isLoadingIndexPattern;
|
||||
const {
|
||||
application: { navigateToUrl },
|
||||
timelines: timelinesUi,
|
||||
|
@ -212,8 +219,6 @@ const DetectionEnginePageComponent: React.FC<DetectionEngineComponentProps> = ({
|
|||
[setShowOnlyThreatIndicatorAlerts]
|
||||
);
|
||||
|
||||
const { indexPattern } = useSourcererDataView(SourcererScopeName.detections);
|
||||
|
||||
const { signalIndexNeedsInit, pollForSignalIndex } = useSignalHelpers();
|
||||
|
||||
const onSkipFocusBeforeEventsTable = useCallback(() => {
|
||||
|
@ -340,6 +345,7 @@ const DetectionEnginePageComponent: React.FC<DetectionEngineComponentProps> = ({
|
|||
filters={alertsHistogramDefaultFilters}
|
||||
query={query}
|
||||
signalIndexName={signalIndexName}
|
||||
runtimeMappings={runtimeMappings}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={2}>
|
||||
|
@ -351,6 +357,7 @@ const DetectionEnginePageComponent: React.FC<DetectionEngineComponentProps> = ({
|
|||
titleSize={'s'}
|
||||
signalIndexName={signalIndexName}
|
||||
updateDateRange={updateDateRangeCallback}
|
||||
runtimeMappings={runtimeMappings}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -208,7 +208,14 @@ const RuleDetailsPageComponent: React.FC<DetectionEngineComponentProps> = ({
|
|||
] = useUserData();
|
||||
const { loading: listsConfigLoading, needsConfiguration: needsListsConfiguration } =
|
||||
useListsConfig();
|
||||
const loading = userInfoLoading || listsConfigLoading;
|
||||
|
||||
const {
|
||||
indexPattern,
|
||||
runtimeMappings,
|
||||
loading: isLoadingIndexPattern,
|
||||
} = useSourcererDataView(SourcererScopeName.detections);
|
||||
|
||||
const loading = userInfoLoading || listsConfigLoading || isLoadingIndexPattern;
|
||||
const { detailName: ruleId } = useParams<{ detailName: string }>();
|
||||
const {
|
||||
rule: maybeRule,
|
||||
|
@ -601,7 +608,6 @@ const RuleDetailsPageComponent: React.FC<DetectionEngineComponentProps> = ({
|
|||
[setShowOnlyThreatIndicatorAlerts]
|
||||
);
|
||||
|
||||
const { indexPattern } = useSourcererDataView(SourcererScopeName.detections);
|
||||
const exceptionLists = useMemo((): {
|
||||
lists: ExceptionListIdentifiers[];
|
||||
allowedExceptionListTypes: ExceptionListTypeEnum[];
|
||||
|
@ -819,6 +825,7 @@ const RuleDetailsPageComponent: React.FC<DetectionEngineComponentProps> = ({
|
|||
signalIndexName={signalIndexName}
|
||||
defaultStackByOption={defaultRuleStackByOption}
|
||||
updateDateRange={updateDateRangeCallback}
|
||||
runtimeMappings={runtimeMappings}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</Display>
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import { transformError } from '@kbn/securitysolution-es-utils';
|
||||
import type { SecuritySolutionPluginRouter } from '../../../../types';
|
||||
import { DETECTION_ENGINE_QUERY_SIGNALS_URL } from '../../../../../common/constants';
|
||||
|
@ -35,7 +36,7 @@ export const querySignalsRoute = (
|
|||
},
|
||||
async (context, request, response) => {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const { query, aggs, _source, track_total_hits, size } = request.body;
|
||||
const { query, aggs, _source, track_total_hits, size, runtime_mappings } = request.body;
|
||||
const siemResponse = buildSiemResponse(response);
|
||||
if (
|
||||
query == null &&
|
||||
|
@ -61,6 +62,7 @@ export const querySignalsRoute = (
|
|||
_source,
|
||||
track_total_hits,
|
||||
size,
|
||||
runtime_mappings: runtime_mappings as MappingRuntimeFields,
|
||||
},
|
||||
ignore_unavailable: true,
|
||||
});
|
||||
|
|
|
@ -106,6 +106,41 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('runtime fields', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/endpoint/resolver/signals');
|
||||
await createSignalsIndex(supertest, log);
|
||||
});
|
||||
after(async () => {
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/endpoint/resolver/signals');
|
||||
await deleteSignalsIndex(supertest, log);
|
||||
});
|
||||
|
||||
it('should be able to filter using a runtime field defined in the request', async () => {
|
||||
const query = {
|
||||
query: {
|
||||
bool: {
|
||||
should: [{ match_phrase: { signal_status_querytime: 'open' } }],
|
||||
},
|
||||
},
|
||||
runtime_mappings: {
|
||||
signal_status_querytime: {
|
||||
type: 'keyword',
|
||||
script: {
|
||||
source: `emit(doc['signal.status'].value)`,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_QUERY_SIGNALS_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(query)
|
||||
.expect(200);
|
||||
expect(body.hits.total.value).to.eql(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('find_alerts_route', () => {
|
||||
describe('validation checks', () => {
|
||||
it('should not give errors when querying and the signals index does not exist yet', async () => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue