mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
# Backport This will backport the following commits from `main` to `8.16`: - [[SecuritySolution] Update entity store source field (#197186)](https://github.com/elastic/kibana/pull/197186) <!--- Backport version: 9.4.3 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Pablo Machado","email":"pablo.nevesmachado@elastic.co"},"sourceCommit":{"committedDate":"2024-10-23T08:05:05Z","message":"[SecuritySolution] Update entity store source field (#197186)\n\n## Summary\r\n\r\nIn this PR the source field will only store the first identified index\r\nfor an entity.\r\nThe PR also updates the entities list panel to display a textual\r\ndescription of the source index and adds a new source field filter.\r\n\r\n\r\n\r\nhttps://github.com/user-attachments/assets/c7aad254-f871-4035-9dac-89decce31a55\r\n\r\n\r\n\r\n### Checklist\r\n\r\nDelete any items that are not applicable to this PR.\r\n\r\n- [x] Any text added follows [EUI's writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\r\nsentence case text and includes [i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Mark Hopkin <mark.hopkin@elastic.co>\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"0bafb9632c2e1b09dd56586f15dca83d8ad5b358","branchLabelMapping":{"^v9.0.0$":"main","^v8.17.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["bug","release_note:skip","v9.0.0","Team: SecuritySolution","Theme: entity_analytics","Feature:Entity Analytics","v8.9.0","Team:Entity Analytics","8.16 candidate","v8.16.0","backport:version","v8.17.0"],"title":"[SecuritySolution] Update entity store source field","number":197186,"url":"https://github.com/elastic/kibana/pull/197186","mergeCommit":{"message":"[SecuritySolution] Update entity store source field (#197186)\n\n## Summary\r\n\r\nIn this PR the source field will only store the first identified index\r\nfor an entity.\r\nThe PR also updates the entities list panel to display a textual\r\ndescription of the source index and adds a new source field filter.\r\n\r\n\r\n\r\nhttps://github.com/user-attachments/assets/c7aad254-f871-4035-9dac-89decce31a55\r\n\r\n\r\n\r\n### Checklist\r\n\r\nDelete any items that are not applicable to this PR.\r\n\r\n- [x] Any text added follows [EUI's writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\r\nsentence case text and includes [i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Mark Hopkin <mark.hopkin@elastic.co>\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"0bafb9632c2e1b09dd56586f15dca83d8ad5b358"}},"sourceBranch":"main","suggestedTargetBranches":["8.9","8.16","8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/197186","number":197186,"mergeCommit":{"message":"[SecuritySolution] Update entity store source field (#197186)\n\n## Summary\r\n\r\nIn this PR the source field will only store the first identified index\r\nfor an entity.\r\nThe PR also updates the entities list panel to display a textual\r\ndescription of the source index and adds a new source field filter.\r\n\r\n\r\n\r\nhttps://github.com/user-attachments/assets/c7aad254-f871-4035-9dac-89decce31a55\r\n\r\n\r\n\r\n### Checklist\r\n\r\nDelete any items that are not applicable to this PR.\r\n\r\n- [x] Any text added follows [EUI's writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\r\nsentence case text and includes [i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Mark Hopkin <mark.hopkin@elastic.co>\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"0bafb9632c2e1b09dd56586f15dca83d8ad5b358"}},{"branch":"8.9","label":"v8.9.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.16","label":"v8.16.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.x","label":"v8.17.0","branchLabelMappingKey":"^v8.17.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Pablo Machado <pablo.nevesmachado@elastic.co>
This commit is contained in:
parent
5daf58c563
commit
f25094f779
19 changed files with 246 additions and 126 deletions
|
@ -30472,9 +30472,7 @@ components:
|
|||
name:
|
||||
type: string
|
||||
source:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
|
@ -30626,9 +30624,7 @@ components:
|
|||
name:
|
||||
type: string
|
||||
source:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
|
|
|
@ -30472,9 +30472,7 @@ components:
|
|||
name:
|
||||
type: string
|
||||
source:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
|
@ -30626,9 +30624,7 @@ components:
|
|||
name:
|
||||
type: string
|
||||
source:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
|
|
|
@ -39302,9 +39302,7 @@ components:
|
|||
name:
|
||||
type: string
|
||||
source:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
|
@ -39456,9 +39454,7 @@ components:
|
|||
name:
|
||||
type: string
|
||||
source:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
|
|
|
@ -39302,9 +39302,7 @@ components:
|
|||
name:
|
||||
type: string
|
||||
source:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
|
@ -39456,9 +39454,7 @@ components:
|
|||
name:
|
||||
type: string
|
||||
source:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
|
|
|
@ -24,7 +24,7 @@ export const UserEntity = z.object({
|
|||
'@timestamp': z.string().datetime(),
|
||||
entity: z.object({
|
||||
name: z.string(),
|
||||
source: z.array(z.string()),
|
||||
source: z.string(),
|
||||
}),
|
||||
user: z.object({
|
||||
full_name: z.array(z.string()).optional(),
|
||||
|
@ -48,7 +48,7 @@ export const HostEntity = z.object({
|
|||
'@timestamp': z.string().datetime(),
|
||||
entity: z.object({
|
||||
name: z.string(),
|
||||
source: z.array(z.string()),
|
||||
source: z.string(),
|
||||
}),
|
||||
host: z.object({
|
||||
hostname: z.array(z.string()).optional(),
|
||||
|
|
|
@ -22,12 +22,10 @@ components:
|
|||
- name
|
||||
- source
|
||||
properties:
|
||||
name:
|
||||
name:
|
||||
type: string
|
||||
source:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
type: string
|
||||
user:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -84,12 +82,10 @@ components:
|
|||
- name
|
||||
- source
|
||||
properties:
|
||||
name:
|
||||
name:
|
||||
type: string
|
||||
source:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
type: string
|
||||
host:
|
||||
type: object
|
||||
properties:
|
||||
|
|
|
@ -910,9 +910,7 @@ components:
|
|||
name:
|
||||
type: string
|
||||
source:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
|
@ -1062,9 +1060,7 @@ components:
|
|||
name:
|
||||
type: string
|
||||
source:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
|
|
|
@ -910,9 +910,7 @@ components:
|
|||
name:
|
||||
type: string
|
||||
source:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
|
@ -1062,9 +1060,7 @@ components:
|
|||
name:
|
||||
type: string
|
||||
source:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
|
|
|
@ -8,30 +8,26 @@
|
|||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { MultiselectFilter } from '../../../../common/components/multiselect_filter';
|
||||
import { EntitySourceTag } from '../types';
|
||||
|
||||
interface SourceFilterProps {
|
||||
selectedItems: EntitySource[];
|
||||
onChange: (selectedItems: EntitySource[]) => void;
|
||||
selectedItems: EntitySourceTag[];
|
||||
onChange: (selectedItems: EntitySourceTag[]) => void;
|
||||
}
|
||||
|
||||
export enum EntitySource {
|
||||
CSV_UPLOAD = 'CSV upload',
|
||||
EVENTS = 'Events',
|
||||
}
|
||||
// TODO Fix the Entity Source field before using it
|
||||
export const EntitySourceFilter: React.FC<SourceFilterProps> = ({ selectedItems, onChange }) => {
|
||||
return (
|
||||
<MultiselectFilter<EntitySource>
|
||||
<MultiselectFilter<EntitySourceTag>
|
||||
title={i18n.translate(
|
||||
'xpack.securitySolution.entityAnalytics.entityStore.entitySource.filterTitle',
|
||||
{
|
||||
defaultMessage: 'Source',
|
||||
}
|
||||
)}
|
||||
items={Object.values(EntitySource)}
|
||||
items={Object.values(EntitySourceTag)}
|
||||
selectedItems={selectedItems}
|
||||
onSelectionChange={onChange}
|
||||
width={140}
|
||||
width={190}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -33,7 +33,7 @@ const responseData: ListEntitiesResponse = {
|
|||
user: { name: entityName },
|
||||
entity: {
|
||||
name: entityName,
|
||||
source: ['source'],
|
||||
source: 'test-index',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
@ -21,12 +21,13 @@ import { EntityType } from '../../../../common/api/entity_analytics/entity_store
|
|||
import type { Criteria } from '../../../explore/components/paginated_table';
|
||||
import { PaginatedTable } from '../../../explore/components/paginated_table';
|
||||
import { SeverityFilter } from '../severity/severity_filter';
|
||||
import type { EntitySource } from './components/entity_source_filter';
|
||||
import { EntitySourceFilter } from './components/entity_source_filter';
|
||||
import { useEntitiesListFilters } from './hooks/use_entities_list_filters';
|
||||
import { AssetCriticalityFilter } from '../asset_criticality/asset_criticality_filter';
|
||||
import { useEntitiesListQuery } from './hooks/use_entities_list_query';
|
||||
import { ENTITIES_LIST_TABLE_ID, rowItems } from './constants';
|
||||
import { useEntitiesListColumns } from './hooks/use_entities_list_columns';
|
||||
import type { EntitySourceTag } from './types';
|
||||
|
||||
export const EntitiesList: React.FC = () => {
|
||||
const { deleteQuery, setQuery, isInitializing, from, to } = useGlobalTime();
|
||||
|
@ -40,7 +41,7 @@ export const EntitiesList: React.FC = () => {
|
|||
|
||||
const [selectedSeverities, setSelectedSeverities] = useState<RiskSeverity[]>([]);
|
||||
const [selectedCriticalities, setSelectedCriticalities] = useState<CriticalityLevels[]>([]);
|
||||
const [selectedSources, _] = useState<EntitySource[]>([]);
|
||||
const [selectedSources, setSelectedSources] = useState<EntitySourceTag[]>([]);
|
||||
|
||||
const filter = useEntitiesListFilters({
|
||||
selectedSeverities,
|
||||
|
@ -147,6 +148,7 @@ export const EntitiesList: React.FC = () => {
|
|||
selectedItems={selectedCriticalities}
|
||||
onChange={setSelectedCriticalities}
|
||||
/>
|
||||
<EntitySourceFilter selectedItems={selectedSources} onChange={setSelectedSources} />
|
||||
</EuiFilterGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -5,40 +5,70 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { isUserEntity } from './helpers';
|
||||
import { isUserEntity, sourceFieldToText } from './helpers';
|
||||
import type {
|
||||
Entity,
|
||||
UserEntity,
|
||||
} from '../../../../common/api/entity_analytics/entity_store/entities/common.gen';
|
||||
import { render } from '@testing-library/react';
|
||||
import { TestProviders } from '@kbn/timelines-plugin/public/mock';
|
||||
|
||||
describe('isUserEntity', () => {
|
||||
it('should return true if the record is a UserEntity', () => {
|
||||
const userEntity: UserEntity = {
|
||||
'@timestamp': '2021-08-02T14:00:00.000Z',
|
||||
user: {
|
||||
name: 'test_user',
|
||||
},
|
||||
entity: {
|
||||
name: 'test_user',
|
||||
source: ['logs-test'],
|
||||
},
|
||||
};
|
||||
describe('helpers', () => {
|
||||
describe('isUserEntity', () => {
|
||||
it('should return true if the record is a UserEntity', () => {
|
||||
const userEntity: UserEntity = {
|
||||
'@timestamp': '2021-08-02T14:00:00.000Z',
|
||||
user: {
|
||||
name: 'test_user',
|
||||
},
|
||||
entity: {
|
||||
name: 'test_user',
|
||||
source: 'logs-test',
|
||||
},
|
||||
};
|
||||
|
||||
expect(isUserEntity(userEntity)).toBe(true);
|
||||
expect(isUserEntity(userEntity)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if the record is not a UserEntity', () => {
|
||||
const nonUserEntity: Entity = {
|
||||
'@timestamp': '2021-08-02T14:00:00.000Z',
|
||||
host: {
|
||||
name: 'test_host',
|
||||
},
|
||||
entity: {
|
||||
name: 'test_host',
|
||||
source: 'logs-test',
|
||||
},
|
||||
};
|
||||
|
||||
expect(isUserEntity(nonUserEntity)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return false if the record is not a UserEntity', () => {
|
||||
const nonUserEntity: Entity = {
|
||||
'@timestamp': '2021-08-02T14:00:00.000Z',
|
||||
host: {
|
||||
name: 'test_host',
|
||||
},
|
||||
entity: {
|
||||
name: 'test_host',
|
||||
source: ['logs-test'],
|
||||
},
|
||||
};
|
||||
describe('sourceFieldToText', () => {
|
||||
it("should return 'Events' if the value isn't risk or asset", () => {
|
||||
const { container } = render(sourceFieldToText('anything'), {
|
||||
wrapper: TestProviders,
|
||||
});
|
||||
|
||||
expect(isUserEntity(nonUserEntity)).toBe(false);
|
||||
expect(container).toHaveTextContent('Events');
|
||||
});
|
||||
|
||||
it("should return 'Risk' if the value is a risk index", () => {
|
||||
const { container } = render(sourceFieldToText('risk-score.risk-score-default'), {
|
||||
wrapper: TestProviders,
|
||||
});
|
||||
|
||||
expect(container).toHaveTextContent('Risk');
|
||||
});
|
||||
|
||||
it("should return 'Asset Criticality' if the value is a asset criticality index", () => {
|
||||
const { container } = render(sourceFieldToText('.asset-criticality.asset-criticality-*'), {
|
||||
wrapper: TestProviders,
|
||||
});
|
||||
|
||||
expect(container).toHaveTextContent('Asset Criticality');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import {
|
||||
ASSET_CRITICALITY_INDEX_PATTERN,
|
||||
RISK_SCORE_INDEX_PATTERN,
|
||||
} from '../../../../common/constants';
|
||||
import type {
|
||||
Entity,
|
||||
UserEntity,
|
||||
} from '../../../../common/api/entity_analytics/entity_store/entities/common.gen';
|
||||
|
||||
export const isUserEntity = (record: Entity): record is UserEntity =>
|
||||
!!(record as UserEntity)?.user;
|
||||
|
||||
export const sourceFieldToText = (source: string) => {
|
||||
if (source.match(`^${RISK_SCORE_INDEX_PATTERN}`)) {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.entityAnalytics.entityStore.helpers.sourceField.riskDescription"
|
||||
defaultMessage="Risk"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (source.match(`^${ASSET_CRITICALITY_INDEX_PATTERN}`)) {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.entityAnalytics.entityStore.helpers.sourceField.criticalityDescription"
|
||||
defaultMessage="Asset Criticality"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.entityAnalytics.entityStore.helpers.sourceField.eventDescription"
|
||||
defaultMessage="Events"
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -17,9 +17,9 @@ import { RiskScoreLevel } from '../../severity/common';
|
|||
import { getEmptyTagValue } from '../../../../common/components/empty_value';
|
||||
import type { Columns } from '../../../../explore/components/paginated_table';
|
||||
import type { Entity } from '../../../../../common/api/entity_analytics/entity_store/entities/common.gen';
|
||||
import type { CriticalityLevels } from '../../../../../common/constants';
|
||||
import { type CriticalityLevels } from '../../../../../common/constants';
|
||||
import { ENTITIES_LIST_TABLE_ID } from '../constants';
|
||||
import { isUserEntity } from '../helpers';
|
||||
import { isUserEntity, sourceFieldToText } from '../helpers';
|
||||
import { CRITICALITY_LEVEL_TITLE } from '../../asset_criticality/translations';
|
||||
|
||||
export type EntitiesListColumns = [
|
||||
|
@ -110,7 +110,7 @@ export const useEntitiesListColumns = (): EntitiesListColumns => {
|
|||
truncateText: { lines: 2 },
|
||||
render: (source: string | undefined) => {
|
||||
if (source != null) {
|
||||
return <span>{source}</span>;
|
||||
return sourceFieldToText(source);
|
||||
}
|
||||
|
||||
return getEmptyTagValue();
|
||||
|
|
|
@ -11,7 +11,7 @@ import { useGlobalFilterQuery } from '../../../../common/hooks/use_global_filter
|
|||
import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
|
||||
import { CriticalityLevels } from '../../../../../common/constants';
|
||||
import { RiskSeverity } from '../../../../../common/search_strategy';
|
||||
import { EntitySource } from '../components/entity_source_filter';
|
||||
import { EntitySourceTag } from '../types';
|
||||
|
||||
jest.mock('../../../../common/hooks/use_global_filter_query');
|
||||
|
||||
|
@ -52,7 +52,6 @@ describe('useEntitiesListFilters', () => {
|
|||
{ term: { 'host.risk.calculated_level': RiskSeverity.High } },
|
||||
{ term: { 'user.risk.calculated_level': RiskSeverity.High } },
|
||||
],
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
@ -72,7 +71,6 @@ describe('useEntitiesListFilters', () => {
|
|||
const expectedFilters: QueryDslQueryContainer[] = [
|
||||
{
|
||||
bool: {
|
||||
minimum_should_match: 1,
|
||||
should: [
|
||||
{
|
||||
term: {
|
||||
|
@ -97,13 +95,48 @@ describe('useEntitiesListFilters', () => {
|
|||
useEntitiesListFilters({
|
||||
selectedSeverities: [],
|
||||
selectedCriticalities: [],
|
||||
selectedSources: [EntitySource.CSV_UPLOAD, EntitySource.EVENTS],
|
||||
selectedSources: [EntitySourceTag.criticality, EntitySourceTag.risk],
|
||||
})
|
||||
);
|
||||
|
||||
const expectedFilters: QueryDslQueryContainer[] = [
|
||||
{ term: { 'entity.source': EntitySource.CSV_UPLOAD } },
|
||||
{ term: { 'entity.source': EntitySource.EVENTS } },
|
||||
{
|
||||
bool: {
|
||||
should: [
|
||||
{ wildcard: { 'entity.source': '.asset-criticality.asset-criticality-*' } },
|
||||
{ wildcard: { 'entity.source': 'risk-score.risk-score-*' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
expect(result.current).toEqual(expectedFilters);
|
||||
});
|
||||
|
||||
it('should return source events filters when events is selected', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useEntitiesListFilters({
|
||||
selectedSeverities: [],
|
||||
selectedCriticalities: [],
|
||||
selectedSources: [EntitySourceTag.events],
|
||||
})
|
||||
);
|
||||
|
||||
const expectedFilters: QueryDslQueryContainer[] = [
|
||||
{
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
bool: {
|
||||
must_not: [
|
||||
{ wildcard: { 'entity.source': '.asset-criticality.asset-criticality-*' } },
|
||||
{ wildcard: { 'entity.source': 'risk-score.risk-score-*' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
expect(result.current).toEqual(expectedFilters);
|
||||
|
@ -132,7 +165,7 @@ describe('useEntitiesListFilters', () => {
|
|||
useEntitiesListFilters({
|
||||
selectedSeverities: [RiskSeverity.Low],
|
||||
selectedCriticalities: [CriticalityLevels.HIGH_IMPACT],
|
||||
selectedSources: [EntitySource.CSV_UPLOAD],
|
||||
selectedSources: [EntitySourceTag.risk],
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -143,16 +176,18 @@ describe('useEntitiesListFilters', () => {
|
|||
{ term: { 'host.risk.calculated_level': RiskSeverity.Low } },
|
||||
{ term: { 'user.risk.calculated_level': RiskSeverity.Low } },
|
||||
],
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
bool: {
|
||||
should: [{ term: { 'asset.criticality': CriticalityLevels.HIGH_IMPACT } }],
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
},
|
||||
{ term: { 'entity.source': EntitySource.CSV_UPLOAD } },
|
||||
{
|
||||
bool: {
|
||||
should: [{ wildcard: { 'entity.source': 'risk-score.risk-score-*' } }],
|
||||
},
|
||||
},
|
||||
globalQuery,
|
||||
];
|
||||
|
||||
|
|
|
@ -7,15 +7,19 @@
|
|||
|
||||
import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
|
||||
import { useMemo } from 'react';
|
||||
import type { CriticalityLevels } from '../../../../../common/constants';
|
||||
import {
|
||||
ASSET_CRITICALITY_INDEX_PATTERN,
|
||||
RISK_SCORE_INDEX_PATTERN,
|
||||
type CriticalityLevels,
|
||||
} from '../../../../../common/constants';
|
||||
import type { RiskSeverity } from '../../../../../common/search_strategy';
|
||||
import { useGlobalFilterQuery } from '../../../../common/hooks/use_global_filter_query';
|
||||
import type { EntitySource } from '../components/entity_source_filter';
|
||||
import { EntitySourceTag } from '../types';
|
||||
|
||||
interface UseEntitiesListFiltersParams {
|
||||
selectedSeverities: RiskSeverity[];
|
||||
selectedCriticalities: CriticalityLevels[];
|
||||
selectedSources: EntitySource[];
|
||||
selectedSources: EntitySourceTag[];
|
||||
}
|
||||
|
||||
export const useEntitiesListFilters = ({
|
||||
|
@ -35,17 +39,20 @@ export const useEntitiesListFilters = ({
|
|||
'asset.criticality': value,
|
||||
},
|
||||
})),
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const sourceFilter: QueryDslQueryContainer[] = selectedSources.map((value) => ({
|
||||
term: {
|
||||
'entity.source': value,
|
||||
},
|
||||
}));
|
||||
const sourceFilter: QueryDslQueryContainer[] = selectedSources.length
|
||||
? [
|
||||
{
|
||||
bool: {
|
||||
should: selectedSources.map((tag) => getSourceTagFilterQuery(tag)),
|
||||
},
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const severityFilter: QueryDslQueryContainer[] = selectedSeverities.length
|
||||
? [
|
||||
|
@ -63,7 +70,6 @@ export const useEntitiesListFilters = ({
|
|||
},
|
||||
},
|
||||
]),
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
},
|
||||
]
|
||||
|
@ -80,3 +86,37 @@ export const useEntitiesListFilters = ({
|
|||
return filterList;
|
||||
}, [globalQuery, selectedCriticalities, selectedSeverities, selectedSources]);
|
||||
};
|
||||
|
||||
const getSourceTagFilterQuery = (tag: EntitySourceTag): QueryDslQueryContainer => {
|
||||
if (tag === EntitySourceTag.risk) {
|
||||
return {
|
||||
wildcard: {
|
||||
'entity.source': RISK_SCORE_INDEX_PATTERN,
|
||||
},
|
||||
};
|
||||
}
|
||||
if (tag === EntitySourceTag.criticality) {
|
||||
return {
|
||||
wildcard: {
|
||||
'entity.source': ASSET_CRITICALITY_INDEX_PATTERN,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
bool: {
|
||||
must_not: [
|
||||
{
|
||||
wildcard: {
|
||||
'entity.source': ASSET_CRITICALITY_INDEX_PATTERN,
|
||||
},
|
||||
},
|
||||
{
|
||||
wildcard: {
|
||||
'entity.source': RISK_SCORE_INDEX_PATTERN,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -5,10 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type {
|
||||
Entity,
|
||||
UserEntity,
|
||||
} from '../../../../common/api/entity_analytics/entity_store/entities/common.gen';
|
||||
|
||||
export const isUserEntity = (record: Entity): record is UserEntity =>
|
||||
!!(record as UserEntity)?.user;
|
||||
export enum EntitySourceTag {
|
||||
'risk' = 'Risk',
|
||||
'criticality' = 'Asset Criticality',
|
||||
'events' = 'Events',
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import type { EntityType } from '../../../../../../common/api/entity_analytics/entity_store';
|
||||
import { getIdentityFieldForEntityType } from '../../utils';
|
||||
import { collectValues, newestValue } from '../definition_utils';
|
||||
import { oldestValue, newestValue } from '../definition_utils';
|
||||
import type { UnitedDefinitionField } from '../types';
|
||||
|
||||
export const getCommonUnitedFieldDefinitions = ({
|
||||
|
@ -19,10 +19,9 @@ export const getCommonUnitedFieldDefinitions = ({
|
|||
}): UnitedDefinitionField[] => {
|
||||
const identityField = getIdentityFieldForEntityType(entityType);
|
||||
return [
|
||||
collectValues({
|
||||
oldestValue({
|
||||
sourceField: '_index',
|
||||
field: 'entity.source',
|
||||
fieldHistoryLength,
|
||||
}),
|
||||
newestValue({ field: 'asset.criticality' }),
|
||||
newestValue({
|
||||
|
|
|
@ -117,8 +117,7 @@ describe('getUnitedEntityDefinition', () => {
|
|||
},
|
||||
Object {
|
||||
"field": "entity.source",
|
||||
"maxLength": 10,
|
||||
"operation": "collect_values",
|
||||
"operation": "prefer_oldest_value",
|
||||
},
|
||||
Object {
|
||||
"field": "asset.criticality",
|
||||
|
@ -219,8 +218,10 @@ describe('getUnitedEntityDefinition', () => {
|
|||
},
|
||||
Object {
|
||||
"aggregation": Object {
|
||||
"limit": 10,
|
||||
"type": "terms",
|
||||
"sort": Object {
|
||||
"@timestamp": "asc",
|
||||
},
|
||||
"type": "top_value",
|
||||
},
|
||||
"destination": "entity.source",
|
||||
"source": "_index",
|
||||
|
@ -373,8 +374,7 @@ describe('getUnitedEntityDefinition', () => {
|
|||
},
|
||||
Object {
|
||||
"field": "entity.source",
|
||||
"maxLength": 10,
|
||||
"operation": "collect_values",
|
||||
"operation": "prefer_oldest_value",
|
||||
},
|
||||
Object {
|
||||
"field": "asset.criticality",
|
||||
|
@ -467,8 +467,10 @@ describe('getUnitedEntityDefinition', () => {
|
|||
},
|
||||
Object {
|
||||
"aggregation": Object {
|
||||
"limit": 10,
|
||||
"type": "terms",
|
||||
"sort": Object {
|
||||
"@timestamp": "asc",
|
||||
},
|
||||
"type": "top_value",
|
||||
},
|
||||
"destination": "entity.source",
|
||||
"source": "_index",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue