mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[8.18] Deprecate universal entity (#211772)
# Backport This will backport the following commits from `main` to `8.18`: - [Deprecate universal entity](https://github.com/elastic/kibana/pull/210978) <!--- Backport version: 9.6.6 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Ido Cohen","email":"90558359+CohenIdo@users.noreply.github.com"},"sourceCommit":{"committedDate":"2025-02-18T14:49:32Z","message":"Deprecate universal entity","sha":"f5c9529e37ea3dddca4db748bfa7e2391303f87e","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","Team:Cloud Security","backport:version","v8.18.0","v9.1.0","v8.19.0"],"title":"Deprecate universal entity","number":210978,"url":"https://github.com/elastic/kibana/pull/210978","mergeCommit":{"message":"Deprecate universal entity","sha":"f5c9529e37ea3dddca4db748bfa7e2391303f87e"}},"sourceBranch":"main","suggestedTargetBranches":["9.0","8.18","8.x"],"targetPullRequestStates":[{"branch":"9.0","label":"v9.0.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.18","label":"v8.18.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/210978","number":210978,"mergeCommit":{"message":"Deprecate universal entity","sha":"f5c9529e37ea3dddca4db748bfa7e2391303f87e"}},{"branch":"8.x","label":"v8.19.0","branchLabelMappingKey":"^v8.19.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
8e26411396
commit
0131c4c71b
29 changed files with 8 additions and 356 deletions
|
@ -104,4 +104,4 @@ enabled:
|
|||
- x-pack/test/cloud_security_posture_functional/config.agentless.ts
|
||||
- x-pack/test/cloud_security_posture_functional/data_views/config.ts
|
||||
- x-pack/test/automatic_import_api_integration/apis/config_basic.ts
|
||||
- x-pack/test/automatic_import_api_integration/apis/config_graphs.ts
|
||||
- x-pack/test/automatic_import_api_integration/apis/config_graphs.ts
|
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -2532,7 +2532,6 @@ x-pack/solutions/security/plugins/security_solution/public/common/components/ses
|
|||
x-pack/solutions/security/plugins/security_solution/public/cloud_defend @elastic/kibana-cloud-security-posture
|
||||
x-pack/solutions/security/plugins/security_solution/public/cloud_security_posture @elastic/kibana-cloud-security-posture
|
||||
x-pack/solutions/security/plugins/security_solution/public/kubernetes @elastic/kibana-cloud-security-posture
|
||||
x-pack/test/security_solution_api_integration/test_suites/asset_inventory @elastic/kibana-cloud-security-posture
|
||||
x-pack/solutions/security/plugins/security_solution/server/lib/asset_inventory @elastic/kibana-cloud-security-posture
|
||||
|
||||
## Fleet plugin (co-owned with Fleet team)
|
||||
|
|
|
@ -50332,7 +50332,6 @@ components:
|
|||
- user
|
||||
- host
|
||||
- service
|
||||
- universal
|
||||
type: string
|
||||
Security_Entity_Analytics_API_HostEntity:
|
||||
type: object
|
||||
|
@ -50408,7 +50407,6 @@ components:
|
|||
- host.name
|
||||
- user.name
|
||||
- service.name
|
||||
- related.entity
|
||||
type: string
|
||||
Security_Entity_Analytics_API_IndexPattern:
|
||||
type: string
|
||||
|
|
|
@ -38799,7 +38799,6 @@ components:
|
|||
- user
|
||||
- host
|
||||
- service
|
||||
- universal
|
||||
type: string
|
||||
Security_Entity_Analytics_API_HostEntity:
|
||||
type: object
|
||||
|
@ -38875,7 +38874,6 @@ components:
|
|||
- host.name
|
||||
- user.name
|
||||
- service.name
|
||||
- related.entity
|
||||
type: string
|
||||
Security_Entity_Analytics_API_IndexPattern:
|
||||
type: string
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
import { z } from '@kbn/zod';
|
||||
|
||||
export type IdField = z.infer<typeof IdField>;
|
||||
export const IdField = z.enum(['host.name', 'user.name', 'service.name', 'related.entity']);
|
||||
export const IdField = z.enum(['host.name', 'user.name', 'service.name']);
|
||||
export type IdFieldEnum = typeof IdField.enum;
|
||||
export const IdFieldEnum = IdField.enum;
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@ components:
|
|||
- 'host.name'
|
||||
- 'user.name'
|
||||
- 'service.name'
|
||||
- 'related.entity'
|
||||
AssetCriticalityRecordIdParts:
|
||||
type: object
|
||||
properties:
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
import { z } from '@kbn/zod';
|
||||
|
||||
export type EntityType = z.infer<typeof EntityType>;
|
||||
export const EntityType = z.enum(['user', 'host', 'service', 'universal']);
|
||||
export const EntityType = z.enum(['user', 'host', 'service']);
|
||||
export type EntityTypeEnum = typeof EntityType.enum;
|
||||
export const EntityTypeEnum = EntityType.enum;
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ components:
|
|||
- user
|
||||
- host
|
||||
- service
|
||||
- universal
|
||||
|
||||
EngineDescriptor:
|
||||
type: object
|
||||
|
|
|
@ -7,17 +7,11 @@
|
|||
|
||||
import type { ExperimentalFeatures } from '../../experimental_features';
|
||||
import { getAllEntityTypes, getDisabledEntityTypes } from '../utils';
|
||||
import { EntityType } from '../types';
|
||||
|
||||
const ASSET_CRITICALITY_UNAVAILABLE_TYPES = [EntityType.universal];
|
||||
|
||||
// TODO delete this function when the universal entity support is added
|
||||
export const getAssetCriticalityEntityTypes = (experimentalFeatures: ExperimentalFeatures) => {
|
||||
const allEntityTypes = getAllEntityTypes();
|
||||
const disabledEntityTypes = getDisabledEntityTypes(experimentalFeatures);
|
||||
|
||||
return allEntityTypes.filter(
|
||||
(value) =>
|
||||
!disabledEntityTypes.includes(value) && !ASSET_CRITICALITY_UNAVAILABLE_TYPES.includes(value)
|
||||
);
|
||||
return allEntityTypes.filter((value) => !disabledEntityTypes.includes(value));
|
||||
};
|
||||
|
|
|
@ -6,18 +6,12 @@
|
|||
*/
|
||||
|
||||
import type { ExperimentalFeatures } from '../../experimental_features';
|
||||
import { EntityType } from '../types';
|
||||
|
||||
import { getAllEntityTypes, getDisabledEntityTypes } from '../utils';
|
||||
|
||||
const ENTITY_STORE_UNAVAILABLE_TYPES = [EntityType.universal];
|
||||
|
||||
// TODO delete this function when the universal entity support is added
|
||||
export const getEnabledStoreEntityTypes = (experimentalFeatures: ExperimentalFeatures) => {
|
||||
const allEntityTypes = getAllEntityTypes();
|
||||
const disabledEntityTypes = getDisabledEntityTypes(experimentalFeatures);
|
||||
|
||||
return allEntityTypes.filter(
|
||||
(value) =>
|
||||
!disabledEntityTypes.includes(value) && !ENTITY_STORE_UNAVAILABLE_TYPES.includes(value)
|
||||
);
|
||||
return allEntityTypes.filter((value) => !disabledEntityTypes.includes(value));
|
||||
};
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
import { EntityType } from '../types';
|
||||
import { getAllEntityTypes, getDisabledEntityTypes } from '../utils';
|
||||
import type { ExperimentalFeatures } from '../../experimental_features';
|
||||
|
||||
|
@ -28,15 +27,10 @@ export function fromEnum<EnumType extends string>(
|
|||
);
|
||||
}
|
||||
|
||||
const RISK_ENGINE_UNAVAILABLE_TYPES = [EntityType.universal];
|
||||
|
||||
// TODO delete this function when the universal entity support is added
|
||||
export const getRiskEngineEntityTypes = (experimentalFeatures: ExperimentalFeatures) => {
|
||||
const allEntityTypes = getAllEntityTypes();
|
||||
const disabledEntityTypes = getDisabledEntityTypes(experimentalFeatures);
|
||||
|
||||
return allEntityTypes.filter(
|
||||
(value) =>
|
||||
!disabledEntityTypes.includes(value) && !RISK_ENGINE_UNAVAILABLE_TYPES.includes(value)
|
||||
);
|
||||
return allEntityTypes.filter((value) => !disabledEntityTypes.includes(value));
|
||||
};
|
||||
|
|
|
@ -15,19 +15,16 @@ export enum EntityType {
|
|||
user = 'user',
|
||||
host = 'host',
|
||||
service = 'service',
|
||||
universal = 'universal',
|
||||
}
|
||||
|
||||
export enum EntityIdentifierFields {
|
||||
hostName = 'host.name',
|
||||
userName = 'user.name',
|
||||
serviceName = 'service.name',
|
||||
universal = 'related.entity',
|
||||
}
|
||||
|
||||
export const EntityTypeToIdentifierField: Record<EntityType, EntityIdentifierFields> = {
|
||||
[EntityType.host]: EntityIdentifierFields.hostName,
|
||||
[EntityType.user]: EntityIdentifierFields.userName,
|
||||
[EntityType.service]: EntityIdentifierFields.serviceName,
|
||||
[EntityType.universal]: EntityIdentifierFields.universal,
|
||||
};
|
||||
|
|
|
@ -4,13 +4,8 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { getAllEntityTypes, getDisabledEntityTypes } from './utils';
|
||||
import { getAllEntityTypes } from './utils';
|
||||
import { EntityType } from './types';
|
||||
import type { ExperimentalFeatures } from '../experimental_features';
|
||||
import { mockGlobalState } from '../../public/common/mock';
|
||||
|
||||
const mockedExperimentalFeatures = mockGlobalState.app.enableExperimental;
|
||||
|
||||
describe('utils', () => {
|
||||
describe('getAllEntityTypes', () => {
|
||||
|
@ -19,24 +14,4 @@ describe('utils', () => {
|
|||
expect(entityTypes).toEqual(Object.values(EntityType));
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDisabledEntityTypes', () => {
|
||||
it('should return disabled entity types when assetInventoryStoreEnabled is false', () => {
|
||||
const experimentalFeatures: ExperimentalFeatures = {
|
||||
...mockedExperimentalFeatures,
|
||||
assetInventoryStoreEnabled: false,
|
||||
};
|
||||
const disabledEntityTypes = getDisabledEntityTypes(experimentalFeatures);
|
||||
expect(disabledEntityTypes).toEqual([EntityType.universal]);
|
||||
});
|
||||
|
||||
it('should return no disabled entity types when both features are true', () => {
|
||||
const experimentalFeatures: ExperimentalFeatures = {
|
||||
...mockedExperimentalFeatures,
|
||||
assetInventoryStoreEnabled: true,
|
||||
};
|
||||
const disabledEntityTypes = getDisabledEntityTypes(experimentalFeatures);
|
||||
expect(disabledEntityTypes).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -13,11 +13,6 @@ export const getDisabledEntityTypes = (
|
|||
experimentalFeatures: ExperimentalFeatures
|
||||
): EntityType[] => {
|
||||
const disabledEntityTypes: EntityType[] = [];
|
||||
const isUniversalEntityStoreEnabled = experimentalFeatures.assetInventoryStoreEnabled;
|
||||
|
||||
if (!isUniversalEntityStoreEnabled) {
|
||||
disabledEntityTypes.push(EntityType.universal);
|
||||
}
|
||||
|
||||
return disabledEntityTypes;
|
||||
};
|
||||
|
|
|
@ -262,12 +262,6 @@ export const allowedExperimentalValues = Object.freeze({
|
|||
*/
|
||||
crowdstrikeRunScriptEnabled: true,
|
||||
|
||||
/**
|
||||
* Enables the Asset Inventory Entity Store feature.
|
||||
* Allows initializing the Universal Entity Store via the API.
|
||||
*/
|
||||
assetInventoryStoreEnabled: false,
|
||||
|
||||
/**
|
||||
* Enables the Asset Inventory feature
|
||||
*/
|
||||
|
|
|
@ -93,12 +93,10 @@ export const EntityTypeToLevelField: Record<EntityType, RiskScoreFields> = {
|
|||
[EntityType.host]: RiskScoreFields.hostRisk,
|
||||
[EntityType.user]: RiskScoreFields.userRisk,
|
||||
[EntityType.service]: RiskScoreFields.serviceRisk,
|
||||
[EntityType.universal]: RiskScoreFields.unsupported, // We don't calculate risk for the universal entity
|
||||
};
|
||||
|
||||
export const EntityTypeToScoreField: Record<EntityType, RiskScoreFields> = {
|
||||
[EntityType.host]: RiskScoreFields.hostRiskScore,
|
||||
[EntityType.user]: RiskScoreFields.userRiskScore,
|
||||
[EntityType.service]: RiskScoreFields.serviceRiskScore,
|
||||
[EntityType.universal]: RiskScoreFields.unsupported, // We don't calculate risk for the universal entity
|
||||
};
|
||||
|
|
|
@ -1204,7 +1204,6 @@ components:
|
|||
- user
|
||||
- host
|
||||
- service
|
||||
- universal
|
||||
type: string
|
||||
HostEntity:
|
||||
type: object
|
||||
|
@ -1280,7 +1279,6 @@ components:
|
|||
- host.name
|
||||
- user.name
|
||||
- service.name
|
||||
- related.entity
|
||||
type: string
|
||||
IndexPattern:
|
||||
type: string
|
||||
|
|
|
@ -1204,7 +1204,6 @@ components:
|
|||
- user
|
||||
- host
|
||||
- service
|
||||
- universal
|
||||
type: string
|
||||
HostEntity:
|
||||
type: object
|
||||
|
@ -1280,7 +1279,6 @@ components:
|
|||
- host.name
|
||||
- user.name
|
||||
- service.name
|
||||
- related.entity
|
||||
type: string
|
||||
IndexPattern:
|
||||
type: string
|
||||
|
|
|
@ -35,7 +35,6 @@ export const EntityIconByType: Record<EntityType, IconType> = {
|
|||
[EntityType.user]: 'user',
|
||||
[EntityType.host]: 'storage',
|
||||
[EntityType.service]: 'node',
|
||||
[EntityType.universal]: 'globe', // random value since we don't support universal entity type
|
||||
};
|
||||
|
||||
export const sourceFieldToText = (source: string) => {
|
||||
|
|
|
@ -32,7 +32,6 @@ export const EntityPanelKeyByType: Record<EntityType, string | undefined> = {
|
|||
[EntityType.host]: HostPanelKey,
|
||||
[EntityType.user]: UserPanelKey,
|
||||
[EntityType.service]: ServicePanelKey,
|
||||
[EntityType.universal]: undefined, // TODO create universal flyout?
|
||||
};
|
||||
|
||||
// TODO rename all params and merged them as 'entityName'
|
||||
|
@ -40,5 +39,4 @@ export const EntityPanelParamByType: Record<EntityType, string | undefined> = {
|
|||
[EntityType.host]: 'hostName',
|
||||
[EntityType.user]: 'userName',
|
||||
[EntityType.service]: 'serviceName',
|
||||
[EntityType.universal]: undefined, // TODO create universal flyout?
|
||||
};
|
||||
|
|
|
@ -82,7 +82,6 @@ const entityTypeByIdField = {
|
|||
'host.name': 'host',
|
||||
'user.name': 'user',
|
||||
'service.name': 'service',
|
||||
'related.entity': 'universal',
|
||||
} as const;
|
||||
|
||||
export const getImplicitEntityFields = (record: AssetCriticalityUpsertWithDeleted) => {
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
import type { EntityType } from '../../../../../common/api/entity_analytics/entity_store';
|
||||
import {
|
||||
HOST_DEFINITION_VERSION,
|
||||
UNIVERSAL_DEFINITION_VERSION,
|
||||
USER_DEFINITION_VERSION,
|
||||
SERVICE_DEFINITION_VERSION,
|
||||
} from './entity_descriptions';
|
||||
|
@ -16,7 +15,6 @@ import {
|
|||
export const VERSIONS_BY_ENTITY_TYPE: Record<EntityType, string> = {
|
||||
host: HOST_DEFINITION_VERSION,
|
||||
user: USER_DEFINITION_VERSION,
|
||||
universal: UNIVERSAL_DEFINITION_VERSION,
|
||||
service: SERVICE_DEFINITION_VERSION,
|
||||
};
|
||||
|
||||
|
|
|
@ -8,5 +8,4 @@
|
|||
export * from './host';
|
||||
export * from './user';
|
||||
export * from './service';
|
||||
export * from './universal';
|
||||
export { getCommonFieldDescriptions } from './common';
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
/*
|
||||
* 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 { IngestProcessorContainer } from '@elastic/elasticsearch/lib/api/types';
|
||||
import type { EntityDescription } from '../types';
|
||||
import { collectValues } from './field_utils';
|
||||
|
||||
export const UNIVERSAL_DEFINITION_VERSION = '1.0.0';
|
||||
export const UNIVERSAL_IDENTITY_FIELD = 'related.entity';
|
||||
|
||||
export const entityMetadataExtractorProcessor = {
|
||||
script: {
|
||||
tag: 'entity_metadata_extractor',
|
||||
on_failure: [
|
||||
{
|
||||
set: {
|
||||
field: 'error.message',
|
||||
value:
|
||||
'Processor {{ _ingest.on_failure_processor_type }} with tag {{ _ingest.on_failure_processor_tag }} in pipeline {{ _ingest.on_failure_pipeline }} failed with message {{ _ingest.on_failure_message }}',
|
||||
},
|
||||
},
|
||||
],
|
||||
lang: 'painless',
|
||||
source: /* java */ `
|
||||
// Array, boolean, integer, ip, bytes, anything that is not a map, is a leaf field
|
||||
void overwriteLeafFields(Object toBeOverwritten, Object toOverwrite) {
|
||||
if (!(toBeOverwritten instanceof Map)) {
|
||||
// We can't override anything that isn't a map
|
||||
return;
|
||||
}
|
||||
if (toOverwrite instanceof Map) {
|
||||
Map mapToBeOverwritten = (Map) toBeOverwritten;
|
||||
for (entryToOverwrite in ((Map) toOverwrite).entrySet()) {
|
||||
String keyToOverwrite = entryToOverwrite.getKey();
|
||||
Object valueToOverwrite = entryToOverwrite.getValue();
|
||||
|
||||
if (valueToOverwrite instanceof Map) {
|
||||
// If no initial value, we just put everything we have to overwrite
|
||||
if (mapToBeOverwritten.get(keyToOverwrite) == null) {
|
||||
mapToBeOverwritten.put(keyToOverwrite, valueToOverwrite)
|
||||
} else {
|
||||
overwriteLeafFields(mapToBeOverwritten.get(keyToOverwrite), valueToOverwrite);
|
||||
}
|
||||
} else {
|
||||
mapToBeOverwritten.put(keyToOverwrite, valueToOverwrite)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def id = ctx.entity.id;
|
||||
Map merged = ctx;
|
||||
for (meta in ctx.collected.metadata) {
|
||||
Object json = Processors.json(meta);
|
||||
if (((Map)json)[id] == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (((Map)json)[id] != null) {
|
||||
overwriteLeafFields(merged, ((Map)json)[id]);
|
||||
}
|
||||
}
|
||||
|
||||
merged.entity.id = id;
|
||||
ctx = merged;
|
||||
`,
|
||||
},
|
||||
};
|
||||
|
||||
export const universalEntityEngineDescription: EntityDescription = {
|
||||
version: UNIVERSAL_DEFINITION_VERSION,
|
||||
entityType: 'universal',
|
||||
identityField: UNIVERSAL_IDENTITY_FIELD,
|
||||
fields: [collectValues({ source: 'entities.keyword', destination: 'collected.metadata' })],
|
||||
settings: {
|
||||
timestampField: 'event.ingested',
|
||||
},
|
||||
pipeline: (processors: IngestProcessorContainer[]) => {
|
||||
const index = processors.findIndex((p) => Boolean(p.enrich));
|
||||
|
||||
if (index === -1) {
|
||||
throw new Error('Enrich processor not found');
|
||||
}
|
||||
|
||||
const init = processors.slice(0, index);
|
||||
const tail = processors.slice(index);
|
||||
const pipe = [...init, entityMetadataExtractorProcessor, ...tail];
|
||||
|
||||
return pipe;
|
||||
},
|
||||
dynamic: true,
|
||||
};
|
|
@ -363,14 +363,5 @@ describe('EntityStoreDataClient', () => {
|
|||
|
||||
expect(spyInit).toHaveBeenCalledWith(EntityType.host, expect.anything(), expect.anything());
|
||||
});
|
||||
|
||||
it('does not enable engine when the given entity type is disabled', async () => {
|
||||
await dataClient.enable({
|
||||
...defaultOptions,
|
||||
entityTypes: [EntityType.universal],
|
||||
});
|
||||
|
||||
expect(spyInit).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -306,12 +306,6 @@ export class EntityStoreDataClient {
|
|||
requestBody: InitEntityEngineRequestBody,
|
||||
{ pipelineDebugMode = false }: { pipelineDebugMode?: boolean } = {}
|
||||
): Promise<InitEntityEngineResponse> {
|
||||
const { experimentalFeatures } = this.options;
|
||||
|
||||
if (entityType === EntityType.universal && !experimentalFeatures.assetInventoryStoreEnabled) {
|
||||
throw new Error('Universal entity store is not enabled');
|
||||
}
|
||||
|
||||
if (!this.options.taskManager) {
|
||||
throw new Error('Task Manager is not available');
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import { generateIndexMappings } from '../elasticsearch_assets';
|
|||
import {
|
||||
hostEntityEngineDescription,
|
||||
userEntityEngineDescription,
|
||||
universalEntityEngineDescription,
|
||||
serviceEntityEngineDescription,
|
||||
} from '../entity_definitions/entity_descriptions';
|
||||
import type { EntityStoreConfig } from '../types';
|
||||
|
@ -27,7 +26,6 @@ import { defaultOptions } from '../constants';
|
|||
const engineDescriptionRegistry: Record<EntityType, EntityDescription> = {
|
||||
host: hostEntityEngineDescription,
|
||||
user: userEntityEngineDescription,
|
||||
universal: universalEntityEngineDescription,
|
||||
service: serviceEntityEngineDescription,
|
||||
};
|
||||
|
||||
|
|
|
@ -1,157 +0,0 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
import { entityMetadataExtractorProcessor } from '@kbn/security-solution-plugin/server/lib/entity_analytics/entity_store/entity_definitions/entity_descriptions/universal';
|
||||
import { dynamicNewestRetentionSteps } from '@kbn/security-solution-plugin/server/lib/entity_analytics/entity_store/field_retention/dynamic_retention';
|
||||
import { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||
import { applyIngestProcessorToDoc } from '../utils/ingest';
|
||||
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const es = getService('es');
|
||||
const log = getService('log');
|
||||
describe('@ess @serverless @skipInServerlessMKI Asset Inventory - universal entity engine pipeline ', () => {
|
||||
describe('Entity metadata extractor processor step', () => {
|
||||
it('should extract metadata from "collected.metadata" and add it to the document', async () => {
|
||||
const metadata = {
|
||||
test: {
|
||||
cloud: { super: 123 },
|
||||
okta: { foo: { baz: { qux: 1 } } },
|
||||
},
|
||||
};
|
||||
const doc = {
|
||||
collected: {
|
||||
metadata: [JSON.stringify(metadata)],
|
||||
},
|
||||
entity: {
|
||||
id: 'test',
|
||||
},
|
||||
};
|
||||
|
||||
const result = await applyIngestProcessorToDoc(
|
||||
[entityMetadataExtractorProcessor],
|
||||
doc,
|
||||
es,
|
||||
log
|
||||
);
|
||||
|
||||
const processed = {
|
||||
...doc,
|
||||
...metadata.test,
|
||||
};
|
||||
|
||||
return expect(result).to.eql(processed);
|
||||
});
|
||||
});
|
||||
|
||||
describe('prefer newest value for dynamic entities', () => {
|
||||
it('should return latest value if no history value', async () => {
|
||||
const metadata = {
|
||||
cloud: { super: 123 },
|
||||
};
|
||||
|
||||
const doc = metadata;
|
||||
|
||||
const processor = dynamicNewestRetentionSteps([]);
|
||||
const result = await applyIngestProcessorToDoc([processor], doc, es, log);
|
||||
|
||||
return expect(result).to.eql(doc);
|
||||
});
|
||||
|
||||
it('should return history value if no latest value is found', async () => {
|
||||
const metadata = {
|
||||
cloud: { super: 123 },
|
||||
};
|
||||
|
||||
const doc = {
|
||||
historical: metadata,
|
||||
};
|
||||
|
||||
const processor = dynamicNewestRetentionSteps([]);
|
||||
const result = await applyIngestProcessorToDoc([processor], doc, es, log);
|
||||
|
||||
return expect(result).to.eql({
|
||||
...doc,
|
||||
...metadata,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return latest value if both historical and latest values exist', async () => {
|
||||
const metadata = {
|
||||
cloud: { super: 123 },
|
||||
};
|
||||
|
||||
const historical = {
|
||||
cloud: { super: 456 },
|
||||
};
|
||||
|
||||
const doc = {
|
||||
historical,
|
||||
...metadata,
|
||||
};
|
||||
|
||||
const processor = dynamicNewestRetentionSteps([]);
|
||||
const result = await applyIngestProcessorToDoc([processor], doc, es, log);
|
||||
|
||||
return expect(result).to.eql(doc);
|
||||
});
|
||||
|
||||
it('should merge nested object preserving historical values not found in latest', async () => {
|
||||
const metadata = {
|
||||
cloud: { host: 'test' },
|
||||
okta: { foo: { bar: { baz: 1 } } },
|
||||
};
|
||||
|
||||
const historical = {
|
||||
cloud: { user: 'agent' },
|
||||
okta: { foo: { bar: { qux: 11 } } },
|
||||
};
|
||||
|
||||
const doc = {
|
||||
historical,
|
||||
...metadata,
|
||||
};
|
||||
|
||||
const processor = dynamicNewestRetentionSteps([]);
|
||||
const result = await applyIngestProcessorToDoc([processor], doc, es, log);
|
||||
|
||||
return expect(result).to.eql({
|
||||
historical,
|
||||
cloud: { host: 'test', user: 'agent' },
|
||||
okta: { foo: { bar: { baz: 1, qux: 11 } } },
|
||||
});
|
||||
});
|
||||
|
||||
it('should ignore historical static fields', async () => {
|
||||
const metadata = {
|
||||
cloud: { host: 'test' },
|
||||
};
|
||||
|
||||
const historical = {
|
||||
static: 'static',
|
||||
cloud: { user: 'agent' },
|
||||
okta: { foo: { bar: { qux: 1 } } },
|
||||
};
|
||||
|
||||
const doc = {
|
||||
historical,
|
||||
...metadata,
|
||||
};
|
||||
|
||||
const staticFields = ['static'];
|
||||
const processor = dynamicNewestRetentionSteps(staticFields);
|
||||
const result = await applyIngestProcessorToDoc([processor], doc, es, log);
|
||||
|
||||
return expect(result).to.eql({
|
||||
historical,
|
||||
cloud: { host: 'test', user: 'agent' },
|
||||
okta: { foo: { bar: { qux: 1 } } },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
|
@ -13,6 +13,5 @@ export default function ({ loadTestFile }: FtrProviderContext) {
|
|||
loadTestFile(require.resolve('./entity_store'));
|
||||
loadTestFile(require.resolve('./field_retention_operators'));
|
||||
loadTestFile(require.resolve('./entity_store_nondefault_spaces'));
|
||||
loadTestFile(require.resolve('./asset_inventory_pipeline'));
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue