Skip to content

Commit

Permalink
[Cloud Posture] fix negated filter in findings (#137021)
Browse files Browse the repository at this point in the history
  • Loading branch information
orouz authored Jul 25, 2022
1 parent 0cc899c commit e33f4b0
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* 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 { CSP_LATEST_FINDINGS_DATA_VIEW } from '../../../common/constants';
import { createStubDataView } from '@kbn/data-views-plugin/common/stubs';
import { DataView } from '@kbn/data-views-plugin/common';
import { getFilters } from './get_filters';

describe('Get Filters', () => {
let dataViewMock: DataView;
const fieldKey = 'some_field_name';

beforeEach(() => {
dataViewMock = createStubDataView({
spec: {
id: CSP_LATEST_FINDINGS_DATA_VIEW,
fields: {
a: {
searchable: false,
aggregatable: false,
name: fieldKey,
type: 'type',
},
},
},
});
});

it('negate an existing filter', () => {
const fields = {
dataView: dataViewMock,
field: fieldKey,
value: 'b',
};
const initialFilters = getFilters({
...fields,
filters: [],
negate: false,
});

expect(initialFilters.length).toBe(1);
expect(initialFilters[0].meta.negate).toBe(false);

const nextFilters = getFilters({
...fields,
filters: initialFilters,
negate: true,
});

expect(nextFilters.length).toBe(1);
expect(nextFilters[0].meta.negate).toBe(true);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* 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 {
type Filter,
buildFilter,
FILTERS,
FilterStateStore,
compareFilters,
FilterCompareOptions,
} from '@kbn/es-query';
import type { Serializable } from '@kbn/utility-types';
import type { FindingsBaseProps } from './types';

const compareOptions: FilterCompareOptions = {
negate: false,
};

/**
* adds a new filter to a new filters array
* removes existing filter if negated filter is added
*
* @returns {Filter[]} a new array of filters to be added back to filterManager
*/
export const getFilters = ({
filters: existingFilters,
dataView,
field,
value,
negate,
}: {
filters: Filter[];
dataView: FindingsBaseProps['dataView'];
field: string;
value: Serializable;
negate: boolean;
}): Filter[] => {
const dataViewField = dataView.getFieldByName(field);
if (!dataViewField) return existingFilters;

const phraseFilter = buildFilter(
dataView,
dataViewField,
FILTERS.PHRASE,
negate,
false,
value,
null,
FilterStateStore.APP_STATE
);

const nextFilters = [
...existingFilters.filter(
// Exclude existing filters that match the newly added 'phraseFilter'
(filter) => !compareFilters(filter, phraseFilter, compareOptions)
),
phraseFilter,
];

return nextFilters;
};
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import type { FindingsBaseURLQuery } from '../types';
import { FindingsDistributionBar } from '../layout/findings_distribution_bar';
import {
getFindingsPageSizeInfo,
addFilter,
getFilters,
getPaginationQuery,
getPaginationTableParams,
useBaseEsQuery,
Expand Down Expand Up @@ -119,7 +119,7 @@ export const LatestFindingsContainer = ({ dataView }: FindingsBaseProps) => {
onAddFilter={(field, value, negate) =>
setUrlQuery({
pageIndex: 0,
filters: addFilter({
filters: getFilters({
filters: urlQuery.filters,
dataView,
field,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { FindingsByResourceQuery, useFindingsByResource } from './use_findings_b
import { FindingsByResourceTable } from './findings_by_resource_table';
import {
getFindingsPageSizeInfo,
addFilter,
getFilters,
getPaginationQuery,
getPaginationTableParams,
useBaseEsQuery,
Expand Down Expand Up @@ -143,7 +143,7 @@ const LatestFindingsByResource = ({ dataView }: FindingsBaseProps) => {
onAddFilter={(field, value, negate) =>
setUrlQuery({
pageIndex: 0,
filters: addFilter({
filters: getFilters({
filters: urlQuery.filters,
dataView,
field,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { useUrlQuery } from '../../../../common/hooks/use_url_query';
import type { FindingsBaseURLQuery, FindingsBaseProps, CspFinding } from '../../types';
import {
getFindingsPageSizeInfo,
addFilter,
getFilters,
getPaginationQuery,
getPaginationTableParams,
useBaseEsQuery,
Expand Down Expand Up @@ -147,7 +147,7 @@ export const ResourceFindings = ({ dataView }: FindingsBaseProps) => {
onAddFilter={(field, value, negate) =>
setUrlQuery({
pageIndex: 0,
filters: addFilter({
filters: getFilters({
filters: urlQuery.filters,
dataView,
field,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@
* 2.0.
*/

import { buildEsQuery, type Filter, buildFilter, FILTERS, FilterStateStore } from '@kbn/es-query';
import { buildEsQuery } from '@kbn/es-query';
import { EuiBasicTableProps, Pagination } from '@elastic/eui';
import { useCallback, useEffect, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import type { estypes } from '@elastic/elasticsearch';
import type { Serializable } from '@kbn/utility-types';
import type { FindingsBaseProps, FindingsBaseURLQuery } from './types';
import { useKibana } from '../../common/hooks/use_kibana';
import { isNonNullable } from '../../../common/utils/helpers';
export { getFilters } from './get_filters';

const getBaseQuery = ({ dataView, query, filters }: FindingsBaseURLQuery & FindingsBaseProps) => {
try {
Expand Down Expand Up @@ -130,38 +129,6 @@ export const getAggregationCount = (buckets: estypes.AggregationsStringRareTerms
};
};

export const addFilter = ({
filters,
dataView,
field,
value,
negate,
}: {
filters: Filter[];
dataView: FindingsBaseProps['dataView'];
field: string;
value: Serializable;
negate: boolean;
}): Filter[] => {
const dataViewField = dataView.getFieldByName(field);
if (!dataViewField) return filters;

const singleValue = Array.isArray(value) ? value[0] : value;

const filter = buildFilter(
dataView,
dataViewField,
FILTERS.PHRASE,
negate,
false,
singleValue,
null,
FilterStateStore.APP_STATE
);

return [...filters, filter].filter(isNonNullable);
};

const FIELDS_WITHOUT_KEYWORD_MAPPING = new Set([
'@timestamp',
'resource.sub_type',
Expand Down

0 comments on commit e33f4b0

Please sign in to comment.