mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Add 'Generic' Entity Engine Definition (#211232)
## Summary Introduce a new Entity Engine Definition called Generic. The larger context on why we are introducing a new entity definition is described on this [private github issue](https://github.com/elastic/security-team/issues/11857). The tldr; is that we would like to have an entity store with all the entities described by the [entity ecs field](https://github.com/elastic/security-team/issues/11857). The decision to call `generic` entity definition comes from the fact that any entity can be described with the `entity` field - user, host, service, database, queue, subscription and so on. Therefore it makes sense to have the concept called `generic` entity, meanwhile the existent entity definitions will be called concrete entities, because they describe a very concrete type of entity (currently user, host, service). Other changes included on this PR: - Don't override `entity.name` with `entity.id`, only set if no value is found - Migrate the usage of `entity.type` as the entity definition type to `entity.EngineMetadata.Type` - Changes touching Entity Analytics code around `getRiskEngineEntityTypes` and `getAssetCriticalityEntityTypes`. There was a somewhat unnecessary and duplicated logic in these functions which essentially described the concrete entity definitions to be used by entity analytics flows. A new function called `getEntityAnalyticsEntityTypes` was introduced which unifies this logic and returns the entity types that Entity Analytics care about. Video of a scroll through the entities processed by the generic entity store, source of the data is cloudbeat asset management integration. https://github.com/user-attachments/assets/450afd05-dee0-4449-aaec-2cd69645d6ec #### How to test: - In Advanced Settings (`/app/management/kibana/settings`), enable `securitySolution:enableAssetInventory` <img width="883" alt="image" src="https://github.com/user-attachments/assets/c342abb2-efb3-40a8-b945-d9558f085f34" /> - In Entity Store management (`/security/entity_analytics_entity_store`) enable entity store <img width="1251" alt="image" src="https://github.com/user-attachments/assets/41f709e1-0aea-47dc-9c98-ffaebf18fdb1" /> - Verify Generic Engine Status <img width="1203" alt="image" src="https://github.com/user-attachments/assets/d26b764a-4695-436e-85f7-e3ed7df5a3be" /> - Ingest documents with `entity.id` and `entity.*` fields. Personally I run `cloudbeat` asset discovery locally - Verify ingested documents in `.entities.v1.latest.security_generic_default` <img width="1496" alt="image" src="https://github.com/user-attachments/assets/88286cb9-38c1-4f9d-83a7-57ba33811c60" /> -- **OBS: Also test enabling the store without the uiSetting enabled, so you can make sure that it doesn't enable** ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md) - [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [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 - [x] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [x] This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The `release_note:breaking` label should be applied in these situations. - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [x] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) ### Identify risks Does this PR introduce any risks? For example, consider risks like hard to test bugs, performance regression, potential of data loss. Describe the risk, its severity, and mitigation for each identified risk. Invite stakeholders and evaluate how to proceed before merging. - [x] I see risk on performance, given the amount of aggregations the generated transform does - tested, although we see a higher spike in CPU than before, it's behind a feature flag and it's going to be used in controlled data sets (entity centric logs that contain `entity.id` field) we decided it's good enough to go. - [ ] Enablement/disablement of entity store in a different uiSetting configuration. - [ ] Enable entity store with `securitySolution:enableAssetInventory` disabled. Then enable `securitySolution:enableAssetInventory` ==> No generic entity definition installed. You can manually install it in the EntityStore status page - [ ] Enable entity store with `securitySolution:enableAssetInventory` enabled. Then disable `securitySolution:enableAssetInventory` definition ==> hanging assets of generic entity store that can be deleted manually --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
89d6dabfc2
commit
b1ffcf3060
60 changed files with 808 additions and 198 deletions
|
@ -62494,6 +62494,13 @@ components:
|
|||
- indexPattern
|
||||
- status
|
||||
- fieldHistoryLength
|
||||
Security_Entity_Analytics_API_EngineMetadata:
|
||||
type: object
|
||||
properties:
|
||||
Type:
|
||||
type: string
|
||||
required:
|
||||
- Type
|
||||
Security_Entity_Analytics_API_EngineStatus:
|
||||
enum:
|
||||
- installing
|
||||
|
@ -62507,6 +62514,7 @@ components:
|
|||
- $ref: '#/components/schemas/Security_Entity_Analytics_API_UserEntity'
|
||||
- $ref: '#/components/schemas/Security_Entity_Analytics_API_HostEntity'
|
||||
- $ref: '#/components/schemas/Security_Entity_Analytics_API_ServiceEntity'
|
||||
- $ref: '#/components/schemas/Security_Entity_Analytics_API_GenericEntity'
|
||||
Security_Entity_Analytics_API_EntityRiskLevels:
|
||||
enum:
|
||||
- Unknown
|
||||
|
@ -62589,7 +62597,42 @@ components:
|
|||
- user
|
||||
- host
|
||||
- service
|
||||
- generic
|
||||
type: string
|
||||
Security_Entity_Analytics_API_GenericEntity:
|
||||
type: object
|
||||
properties:
|
||||
'@timestamp':
|
||||
format: date-time
|
||||
type: string
|
||||
asset:
|
||||
type: object
|
||||
properties:
|
||||
criticality:
|
||||
$ref: '#/components/schemas/Security_Entity_Analytics_API_AssetCriticalityLevel'
|
||||
required:
|
||||
- criticality
|
||||
entity:
|
||||
type: object
|
||||
properties:
|
||||
category:
|
||||
type: string
|
||||
EngineMetadata:
|
||||
$ref: '#/components/schemas/Security_Entity_Analytics_API_EngineMetadata'
|
||||
id:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
- type
|
||||
required:
|
||||
- entity
|
||||
Security_Entity_Analytics_API_HostEntity:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -62606,13 +62649,18 @@ components:
|
|||
entity:
|
||||
type: object
|
||||
properties:
|
||||
EngineMetadata:
|
||||
$ref: '#/components/schemas/Security_Entity_Analytics_API_EngineMetadata'
|
||||
name:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
- type
|
||||
event:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -62664,6 +62712,7 @@ components:
|
|||
- host.name
|
||||
- user.name
|
||||
- service.name
|
||||
- entity.id
|
||||
type: string
|
||||
Security_Entity_Analytics_API_IndexPattern:
|
||||
type: string
|
||||
|
@ -62765,13 +62814,18 @@ components:
|
|||
entity:
|
||||
type: object
|
||||
properties:
|
||||
EngineMetadata:
|
||||
$ref: '#/components/schemas/Security_Entity_Analytics_API_EngineMetadata'
|
||||
name:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
- type
|
||||
event:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -62879,13 +62933,18 @@ components:
|
|||
entity:
|
||||
type: object
|
||||
properties:
|
||||
EngineMetadata:
|
||||
$ref: '#/components/schemas/Security_Entity_Analytics_API_EngineMetadata'
|
||||
name:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
- type
|
||||
event:
|
||||
type: object
|
||||
properties:
|
||||
|
|
|
@ -71979,6 +71979,13 @@ components:
|
|||
- indexPattern
|
||||
- status
|
||||
- fieldHistoryLength
|
||||
Security_Entity_Analytics_API_EngineMetadata:
|
||||
type: object
|
||||
properties:
|
||||
Type:
|
||||
type: string
|
||||
required:
|
||||
- Type
|
||||
Security_Entity_Analytics_API_EngineStatus:
|
||||
enum:
|
||||
- installing
|
||||
|
@ -71992,6 +71999,7 @@ components:
|
|||
- $ref: '#/components/schemas/Security_Entity_Analytics_API_UserEntity'
|
||||
- $ref: '#/components/schemas/Security_Entity_Analytics_API_HostEntity'
|
||||
- $ref: '#/components/schemas/Security_Entity_Analytics_API_ServiceEntity'
|
||||
- $ref: '#/components/schemas/Security_Entity_Analytics_API_GenericEntity'
|
||||
Security_Entity_Analytics_API_EntityRiskLevels:
|
||||
enum:
|
||||
- Unknown
|
||||
|
@ -72074,7 +72082,42 @@ components:
|
|||
- user
|
||||
- host
|
||||
- service
|
||||
- generic
|
||||
type: string
|
||||
Security_Entity_Analytics_API_GenericEntity:
|
||||
type: object
|
||||
properties:
|
||||
'@timestamp':
|
||||
format: date-time
|
||||
type: string
|
||||
asset:
|
||||
type: object
|
||||
properties:
|
||||
criticality:
|
||||
$ref: '#/components/schemas/Security_Entity_Analytics_API_AssetCriticalityLevel'
|
||||
required:
|
||||
- criticality
|
||||
entity:
|
||||
type: object
|
||||
properties:
|
||||
category:
|
||||
type: string
|
||||
EngineMetadata:
|
||||
$ref: '#/components/schemas/Security_Entity_Analytics_API_EngineMetadata'
|
||||
id:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
- type
|
||||
required:
|
||||
- entity
|
||||
Security_Entity_Analytics_API_HostEntity:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -72091,13 +72134,18 @@ components:
|
|||
entity:
|
||||
type: object
|
||||
properties:
|
||||
EngineMetadata:
|
||||
$ref: '#/components/schemas/Security_Entity_Analytics_API_EngineMetadata'
|
||||
name:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
- type
|
||||
event:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -72149,6 +72197,7 @@ components:
|
|||
- host.name
|
||||
- user.name
|
||||
- service.name
|
||||
- entity.id
|
||||
type: string
|
||||
Security_Entity_Analytics_API_IndexPattern:
|
||||
type: string
|
||||
|
@ -72250,13 +72299,18 @@ components:
|
|||
entity:
|
||||
type: object
|
||||
properties:
|
||||
EngineMetadata:
|
||||
$ref: '#/components/schemas/Security_Entity_Analytics_API_EngineMetadata'
|
||||
name:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
- type
|
||||
event:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -72364,13 +72418,18 @@ components:
|
|||
entity:
|
||||
type: object
|
||||
properties:
|
||||
EngineMetadata:
|
||||
$ref: '#/components/schemas/Security_Entity_Analytics_API_EngineMetadata'
|
||||
name:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
- type
|
||||
event:
|
||||
type: object
|
||||
properties:
|
||||
|
|
|
@ -21,6 +21,22 @@ const entity = {
|
|||
},
|
||||
};
|
||||
|
||||
const entityWithEngineMetadata = {
|
||||
entity: {
|
||||
last_seen_timestamp: '2024-08-06T17:03:50.722Z',
|
||||
schema_version: 'v1',
|
||||
definition_version: '999.999.999',
|
||||
display_name: 'message_processor',
|
||||
identity_fields: ['log.logger', 'event.category'],
|
||||
id: '6UHVPiduEC2qk6rMjs1Jzg==',
|
||||
metrics: {},
|
||||
definition_id: 'admin-console-services',
|
||||
EngineMetadata: {
|
||||
Type: 'service',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const metadata = {
|
||||
host: {
|
||||
os: {
|
||||
|
@ -68,5 +84,15 @@ describe('Entity Schemas', () => {
|
|||
const result = entityLatestSchema.safeParse(doc);
|
||||
expect(result).toHaveProperty('success', true);
|
||||
});
|
||||
|
||||
it('should parse an entity with metadata and engine metadata', () => {
|
||||
const doc = {
|
||||
...entityWithEngineMetadata,
|
||||
...metadata,
|
||||
};
|
||||
|
||||
const result = entityLatestSchema.safeParse(doc);
|
||||
expect(result).toHaveProperty('success', true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,13 +10,18 @@ import { arrayOfStringsSchema } from './common';
|
|||
|
||||
export const entityBaseSchema = z.object({
|
||||
id: z.string(),
|
||||
type: z.string(),
|
||||
type: z.optional(z.string()),
|
||||
identity_fields: z.union([arrayOfStringsSchema, z.string()]),
|
||||
display_name: z.string(),
|
||||
metrics: z.optional(z.record(z.string(), z.number())),
|
||||
definition_version: z.string(),
|
||||
schema_version: z.string(),
|
||||
definition_id: z.string(),
|
||||
EngineMetadata: z.optional(
|
||||
z.object({
|
||||
Type: z.string(),
|
||||
})
|
||||
),
|
||||
});
|
||||
|
||||
export interface MetadataRecord {
|
||||
|
|
|
@ -10,7 +10,7 @@ Array [
|
|||
},
|
||||
Object {
|
||||
"set": Object {
|
||||
"field": "entity.type",
|
||||
"field": "entity.EngineMetadata.Type",
|
||||
"value": "service",
|
||||
},
|
||||
},
|
||||
|
@ -115,7 +115,7 @@ Array [
|
|||
},
|
||||
Object {
|
||||
"set": Object {
|
||||
"field": "entity.type",
|
||||
"field": "entity.EngineMetadata.Type",
|
||||
"value": "service",
|
||||
},
|
||||
},
|
||||
|
|
|
@ -111,7 +111,7 @@ export function generateLatestProcessors(definition: EntityDefinition) {
|
|||
},
|
||||
{
|
||||
set: {
|
||||
field: 'entity.type',
|
||||
field: 'entity.EngineMetadata.Type',
|
||||
value: definition.type,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -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']);
|
||||
export const IdField = z.enum(['host.name', 'user.name', 'service.name', 'entity.id']);
|
||||
export type IdFieldEnum = typeof IdField.enum;
|
||||
export const IdFieldEnum = IdField.enum;
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ components:
|
|||
- 'host.name'
|
||||
- 'user.name'
|
||||
- 'service.name'
|
||||
- 'entity.id'
|
||||
AssetCriticalityRecordIdParts:
|
||||
type: object
|
||||
properties:
|
||||
|
|
|
@ -40,7 +40,7 @@ export const AfterKeys = z.object({
|
|||
host: EntityAfterKey.optional(),
|
||||
user: EntityAfterKey.optional(),
|
||||
service: EntityAfterKey.optional(),
|
||||
universal: EntityAfterKey.optional(),
|
||||
generic: EntityAfterKey.optional(),
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -74,7 +74,7 @@ export const DateRange = z.object({
|
|||
});
|
||||
|
||||
export type IdentifierType = z.infer<typeof IdentifierType>;
|
||||
export const IdentifierType = z.enum(['host', 'user', 'service', 'universal']);
|
||||
export const IdentifierType = z.enum(['host', 'user', 'service', 'generic']);
|
||||
export type IdentifierTypeEnum = typeof IdentifierType.enum;
|
||||
export const IdentifierTypeEnum = IdentifierType.enum;
|
||||
|
||||
|
@ -177,7 +177,7 @@ export const RiskScoreWeightInternal = z.union([
|
|||
host: RiskScoreEntityIdentifierWeights,
|
||||
user: RiskScoreEntityIdentifierWeights.optional(),
|
||||
service: RiskScoreEntityIdentifierWeights.optional(),
|
||||
universal: RiskScoreEntityIdentifierWeights.optional(),
|
||||
generic: RiskScoreEntityIdentifierWeights.optional(),
|
||||
})
|
||||
),
|
||||
RiskScoreWeightGlobalShared.merge(
|
||||
|
@ -185,7 +185,7 @@ export const RiskScoreWeightInternal = z.union([
|
|||
host: RiskScoreEntityIdentifierWeights.optional(),
|
||||
user: RiskScoreEntityIdentifierWeights,
|
||||
service: RiskScoreEntityIdentifierWeights.optional(),
|
||||
universal: RiskScoreEntityIdentifierWeights.optional(),
|
||||
generic: RiskScoreEntityIdentifierWeights.optional(),
|
||||
})
|
||||
),
|
||||
RiskScoreWeightGlobalShared.merge(
|
||||
|
@ -193,7 +193,7 @@ export const RiskScoreWeightInternal = z.union([
|
|||
host: RiskScoreEntityIdentifierWeights.optional(),
|
||||
user: RiskScoreEntityIdentifierWeights.optional(),
|
||||
service: RiskScoreEntityIdentifierWeights,
|
||||
universal: RiskScoreEntityIdentifierWeights.optional(),
|
||||
generic: RiskScoreEntityIdentifierWeights.optional(),
|
||||
})
|
||||
),
|
||||
]);
|
||||
|
|
|
@ -54,7 +54,7 @@ components:
|
|||
$ref: '#/components/schemas/EntityAfterKey'
|
||||
service:
|
||||
$ref: '#/components/schemas/EntityAfterKey'
|
||||
universal:
|
||||
generic:
|
||||
$ref: '#/components/schemas/EntityAfterKey'
|
||||
example:
|
||||
host:
|
||||
|
@ -102,7 +102,7 @@ components:
|
|||
- host
|
||||
- user
|
||||
- service
|
||||
- universal
|
||||
- generic
|
||||
|
||||
RiskScoreInput:
|
||||
description: A generic representation of a document contributing to a Risk Score.
|
||||
|
@ -258,7 +258,7 @@ components:
|
|||
$ref: '#/components/schemas/RiskScoreEntityIdentifierWeights'
|
||||
service:
|
||||
$ref: '#/components/schemas/RiskScoreEntityIdentifierWeights'
|
||||
universal:
|
||||
generic:
|
||||
$ref: '#/components/schemas/RiskScoreEntityIdentifierWeights'
|
||||
|
||||
- allOf:
|
||||
|
@ -273,7 +273,7 @@ components:
|
|||
$ref: '#/components/schemas/RiskScoreEntityIdentifierWeights'
|
||||
service:
|
||||
$ref: '#/components/schemas/RiskScoreEntityIdentifierWeights'
|
||||
universal:
|
||||
generic:
|
||||
$ref: '#/components/schemas/RiskScoreEntityIdentifierWeights'
|
||||
- allOf:
|
||||
- $ref: '#/components/schemas/RiskScoreWeightGlobalShared'
|
||||
|
@ -287,7 +287,7 @@ components:
|
|||
$ref: '#/components/schemas/RiskScoreEntityIdentifierWeights'
|
||||
service:
|
||||
$ref: '#/components/schemas/RiskScoreEntityIdentifierWeights'
|
||||
universal:
|
||||
generic:
|
||||
$ref: '#/components/schemas/RiskScoreEntityIdentifierWeights'
|
||||
RiskScoreWeights:
|
||||
description: 'A list of weights to be applied to the scoring calculation.'
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
import { z } from '@kbn/zod';
|
||||
|
||||
export type EntityType = z.infer<typeof EntityType>;
|
||||
export const EntityType = z.enum(['user', 'host', 'service']);
|
||||
export const EntityType = z.enum(['user', 'host', 'service', 'generic']);
|
||||
export type EntityTypeEnum = typeof EntityType.enum;
|
||||
export const EntityTypeEnum = EntityType.enum;
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ components:
|
|||
- user
|
||||
- host
|
||||
- service
|
||||
- generic
|
||||
|
||||
EngineDescriptor:
|
||||
type: object
|
||||
|
|
|
@ -19,12 +19,19 @@ import { z } from '@kbn/zod';
|
|||
import { EntityRiskScoreRecord } from '../../common/common.gen';
|
||||
import { AssetCriticalityLevel } from '../../asset_criticality/common.gen';
|
||||
|
||||
export type EngineMetadata = z.infer<typeof EngineMetadata>;
|
||||
export const EngineMetadata = z.object({
|
||||
Type: z.string(),
|
||||
});
|
||||
|
||||
export type UserEntity = z.infer<typeof UserEntity>;
|
||||
export const UserEntity = z.object({
|
||||
'@timestamp': z.string().datetime().optional(),
|
||||
entity: z.object({
|
||||
EngineMetadata: EngineMetadata.optional(),
|
||||
name: z.string(),
|
||||
source: z.string(),
|
||||
type: z.string(),
|
||||
}),
|
||||
user: z.object({
|
||||
full_name: z.array(z.string()).optional(),
|
||||
|
@ -52,8 +59,10 @@ export type HostEntity = z.infer<typeof HostEntity>;
|
|||
export const HostEntity = z.object({
|
||||
'@timestamp': z.string().datetime().optional(),
|
||||
entity: z.object({
|
||||
EngineMetadata: EngineMetadata.optional(),
|
||||
name: z.string(),
|
||||
source: z.string(),
|
||||
type: z.string(),
|
||||
}),
|
||||
host: z.object({
|
||||
hostname: z.array(z.string()).optional(),
|
||||
|
@ -82,8 +91,10 @@ export type ServiceEntity = z.infer<typeof ServiceEntity>;
|
|||
export const ServiceEntity = z.object({
|
||||
'@timestamp': z.string().datetime().optional(),
|
||||
entity: z.object({
|
||||
EngineMetadata: EngineMetadata.optional(),
|
||||
name: z.string(),
|
||||
source: z.string(),
|
||||
type: z.string(),
|
||||
}),
|
||||
service: z.object({
|
||||
name: z.string(),
|
||||
|
@ -101,7 +112,25 @@ export const ServiceEntity = z.object({
|
|||
.optional(),
|
||||
});
|
||||
|
||||
export const EntityInternal = z.union([UserEntity, HostEntity, ServiceEntity]);
|
||||
export type GenericEntity = z.infer<typeof GenericEntity>;
|
||||
export const GenericEntity = z.object({
|
||||
'@timestamp': z.string().datetime().optional(),
|
||||
entity: z.object({
|
||||
EngineMetadata: EngineMetadata.optional(),
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
type: z.string(),
|
||||
category: z.string().optional(),
|
||||
source: z.string().optional(),
|
||||
}),
|
||||
asset: z
|
||||
.object({
|
||||
criticality: AssetCriticalityLevel,
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export const EntityInternal = z.union([UserEntity, HostEntity, ServiceEntity, GenericEntity]);
|
||||
|
||||
export type Entity = z.infer<typeof EntityInternal>;
|
||||
export const Entity = EntityInternal as z.ZodType<Entity>;
|
||||
|
|
|
@ -6,6 +6,13 @@ info:
|
|||
paths: {}
|
||||
components:
|
||||
schemas:
|
||||
EngineMetadata:
|
||||
type: object
|
||||
required:
|
||||
- Type
|
||||
properties:
|
||||
Type:
|
||||
type: string
|
||||
UserEntity:
|
||||
type: object
|
||||
required:
|
||||
|
@ -20,11 +27,16 @@ components:
|
|||
required:
|
||||
- name
|
||||
- source
|
||||
- type
|
||||
properties:
|
||||
"EngineMetadata":
|
||||
$ref: '#/components/schemas/EngineMetadata'
|
||||
name:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
user:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -85,11 +97,16 @@ components:
|
|||
required:
|
||||
- name
|
||||
- source
|
||||
- type
|
||||
properties:
|
||||
"EngineMetadata":
|
||||
$ref: '#/components/schemas/EngineMetadata'
|
||||
name:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
host:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -154,11 +171,16 @@ components:
|
|||
required:
|
||||
- name
|
||||
- source
|
||||
- type
|
||||
properties:
|
||||
"EngineMetadata":
|
||||
$ref: '#/components/schemas/EngineMetadata'
|
||||
name:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
service:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -181,8 +203,50 @@ components:
|
|||
ingested:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
# The Generic Entity definition maps more than just entity.
|
||||
# however I don't see a reason to duplicate the definition
|
||||
# of all the fields just for the sake of doing.
|
||||
# Thus the current mapping maps entity and asset only
|
||||
# (used in code). If you end up needing the fields mapped
|
||||
# in the schema just add it.
|
||||
GenericEntity:
|
||||
type: object
|
||||
required:
|
||||
- entity
|
||||
properties:
|
||||
"@timestamp":
|
||||
type: string
|
||||
format: date-time
|
||||
entity:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
- type
|
||||
properties:
|
||||
"EngineMetadata":
|
||||
$ref: '#/components/schemas/EngineMetadata'
|
||||
id:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
category:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
asset:
|
||||
type: object
|
||||
properties:
|
||||
criticality:
|
||||
$ref: '../../asset_criticality/common.schema.yaml#/components/schemas/AssetCriticalityLevel'
|
||||
required:
|
||||
- criticality
|
||||
Entity:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/UserEntity'
|
||||
- $ref: '#/components/schemas/HostEntity'
|
||||
- $ref: '#/components/schemas/ServiceEntity'
|
||||
- $ref: '#/components/schemas/GenericEntity'
|
||||
|
|
|
@ -47,9 +47,9 @@ export const RiskScoresCalculationResponse = z.object({
|
|||
*/
|
||||
service: z.array(EntityRiskScoreRecord).optional(),
|
||||
/**
|
||||
* A list of universal risk scores
|
||||
* A list of generic risk scores
|
||||
*/
|
||||
universal: z.array(EntityRiskScoreRecord).optional(),
|
||||
generic: z.array(EntityRiskScoreRecord).optional(),
|
||||
/**
|
||||
* If 'wait_for' the request will wait for the index refresh.
|
||||
*/
|
||||
|
|
|
@ -45,11 +45,11 @@ components:
|
|||
items:
|
||||
$ref: '../common/common.schema.yaml#/components/schemas/EntityRiskScoreRecord'
|
||||
description: A list of service risk scores
|
||||
universal:
|
||||
generic:
|
||||
type: array
|
||||
items:
|
||||
$ref: '../common/common.schema.yaml#/components/schemas/EntityRiskScoreRecord'
|
||||
description: A list of universal risk scores
|
||||
description: A list of generic risk scores
|
||||
refresh:
|
||||
type: string
|
||||
enum: [wait_for]
|
||||
|
|
|
@ -94,9 +94,9 @@ export const RiskScoresPreviewResponse = z.object({
|
|||
*/
|
||||
service: z.array(EntityRiskScoreRecord).optional(),
|
||||
/**
|
||||
* A list of universal risk scores
|
||||
* A list of generic entities risk scores
|
||||
*/
|
||||
universal: z.array(EntityRiskScoreRecord).optional(),
|
||||
generic: z.array(EntityRiskScoreRecord).optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
|
|
|
@ -106,8 +106,8 @@ components:
|
|||
items:
|
||||
$ref: '../common/common.schema.yaml#/components/schemas/EntityRiskScoreRecord'
|
||||
description: A list of service risk scores
|
||||
universal:
|
||||
generic:
|
||||
type: array
|
||||
items:
|
||||
$ref: '../common/common.schema.yaml#/components/schemas/EntityRiskScoreRecord'
|
||||
description: A list of universal risk scores
|
||||
description: A list of generic entities risk scores
|
|
@ -5,13 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { mockGlobalState } from '../../../public/common/mock';
|
||||
import { parseAssetCriticalityCsvRow } from './parse_asset_criticality_csv_row';
|
||||
|
||||
const experimentalFeatures = mockGlobalState.app.enableExperimental;
|
||||
describe('parseAssetCriticalityCsvRow', () => {
|
||||
it('should return valid false if the row has no columns', () => {
|
||||
const result = parseAssetCriticalityCsvRow([], experimentalFeatures);
|
||||
const result = parseAssetCriticalityCsvRow([]);
|
||||
expect(result.valid).toBe(false);
|
||||
|
||||
// @ts-ignore result can now only be InvalidRecord
|
||||
|
@ -19,7 +17,7 @@ describe('parseAssetCriticalityCsvRow', () => {
|
|||
});
|
||||
|
||||
it('should return valid false if the row has 2 columns', () => {
|
||||
const result = parseAssetCriticalityCsvRow(['host', 'host-1'], experimentalFeatures);
|
||||
const result = parseAssetCriticalityCsvRow(['host', 'host-1']);
|
||||
expect(result.valid).toBe(false);
|
||||
|
||||
// @ts-ignore result can now only be InvalidRecord
|
||||
|
@ -27,10 +25,7 @@ describe('parseAssetCriticalityCsvRow', () => {
|
|||
});
|
||||
|
||||
it('should return valid false if the row has 4 columns', () => {
|
||||
const result = parseAssetCriticalityCsvRow(
|
||||
['host', 'host-1', 'low_impact', 'extra'],
|
||||
experimentalFeatures
|
||||
);
|
||||
const result = parseAssetCriticalityCsvRow(['host', 'host-1', 'low_impact', 'extra']);
|
||||
expect(result.valid).toBe(false);
|
||||
|
||||
// @ts-ignore result can now only be InvalidRecord
|
||||
|
@ -38,7 +33,7 @@ describe('parseAssetCriticalityCsvRow', () => {
|
|||
});
|
||||
|
||||
it('should return valid false if the entity type is missing', () => {
|
||||
const result = parseAssetCriticalityCsvRow(['', 'host-1', 'low_impact'], experimentalFeatures);
|
||||
const result = parseAssetCriticalityCsvRow(['', 'host-1', 'low_impact']);
|
||||
expect(result.valid).toBe(false);
|
||||
|
||||
// @ts-ignore result can now only be InvalidRecord
|
||||
|
@ -46,10 +41,7 @@ describe('parseAssetCriticalityCsvRow', () => {
|
|||
});
|
||||
|
||||
it('should return valid false if the entity type is invalid', () => {
|
||||
const result = parseAssetCriticalityCsvRow(
|
||||
['invalid', 'host-1', 'low_impact'],
|
||||
experimentalFeatures
|
||||
);
|
||||
const result = parseAssetCriticalityCsvRow(['invalid', 'host-1', 'low_impact']);
|
||||
expect(result.valid).toBe(false);
|
||||
|
||||
// @ts-ignore result can now only be InvalidRecord
|
||||
|
@ -60,10 +52,7 @@ describe('parseAssetCriticalityCsvRow', () => {
|
|||
|
||||
it('should return valid false if the entity type is invalid and only log 1000 characters', () => {
|
||||
const invalidEntityType = 'x'.repeat(1001);
|
||||
const result = parseAssetCriticalityCsvRow(
|
||||
[invalidEntityType, 'host-1', 'low_impact'],
|
||||
experimentalFeatures
|
||||
);
|
||||
const result = parseAssetCriticalityCsvRow([invalidEntityType, 'host-1', 'low_impact']);
|
||||
expect(result.valid).toBe(false);
|
||||
|
||||
// @ts-ignore result can now only be InvalidRecord
|
||||
|
@ -73,7 +62,7 @@ describe('parseAssetCriticalityCsvRow', () => {
|
|||
});
|
||||
|
||||
it('should return valid false if the ID is missing', () => {
|
||||
const result = parseAssetCriticalityCsvRow(['host', '', 'low_impact'], experimentalFeatures);
|
||||
const result = parseAssetCriticalityCsvRow(['host', '', 'low_impact']);
|
||||
expect(result.valid).toBe(false);
|
||||
|
||||
// @ts-ignore result can now only be InvalidRecord
|
||||
|
@ -81,7 +70,7 @@ describe('parseAssetCriticalityCsvRow', () => {
|
|||
});
|
||||
|
||||
it('should return valid false if the criticality level is missing', () => {
|
||||
const result = parseAssetCriticalityCsvRow(['host', 'host-1', ''], experimentalFeatures);
|
||||
const result = parseAssetCriticalityCsvRow(['host', 'host-1', '']);
|
||||
expect(result.valid).toBe(false);
|
||||
|
||||
// @ts-ignore result can now only be InvalidRecord
|
||||
|
@ -89,10 +78,7 @@ describe('parseAssetCriticalityCsvRow', () => {
|
|||
});
|
||||
|
||||
it('should return valid false if the criticality level is invalid', () => {
|
||||
const result = parseAssetCriticalityCsvRow(
|
||||
['host', 'host-1', 'unassigned_impact'],
|
||||
experimentalFeatures
|
||||
);
|
||||
const result = parseAssetCriticalityCsvRow(['host', 'host-1', 'unassigned_impact']);
|
||||
expect(result.valid).toBe(false);
|
||||
|
||||
// @ts-ignore result can now only be InvalidRecord
|
||||
|
@ -103,10 +89,7 @@ describe('parseAssetCriticalityCsvRow', () => {
|
|||
|
||||
it('should return valid false if the criticality level is invalid and only log 1000 characters', () => {
|
||||
const invalidCriticalityLevel = 'x'.repeat(1001);
|
||||
const result = parseAssetCriticalityCsvRow(
|
||||
['host', 'host-1', invalidCriticalityLevel],
|
||||
experimentalFeatures
|
||||
);
|
||||
const result = parseAssetCriticalityCsvRow(['host', 'host-1', invalidCriticalityLevel]);
|
||||
expect(result.valid).toBe(false);
|
||||
|
||||
// @ts-ignore result can now only be InvalidRecord
|
||||
|
@ -117,10 +100,7 @@ describe('parseAssetCriticalityCsvRow', () => {
|
|||
|
||||
it('should return valid false if the ID is too long', () => {
|
||||
const idValue = 'x'.repeat(1001);
|
||||
const result = parseAssetCriticalityCsvRow(
|
||||
['host', idValue, 'low_impact'],
|
||||
experimentalFeatures
|
||||
);
|
||||
const result = parseAssetCriticalityCsvRow(['host', idValue, 'low_impact']);
|
||||
expect(result.valid).toBe(false);
|
||||
|
||||
// @ts-ignore result can now only be InvalidRecord
|
||||
|
@ -130,9 +110,7 @@ describe('parseAssetCriticalityCsvRow', () => {
|
|||
});
|
||||
|
||||
it('should return the parsed row', () => {
|
||||
expect(
|
||||
parseAssetCriticalityCsvRow(['host', 'host-1', 'low_impact'], experimentalFeatures)
|
||||
).toEqual({
|
||||
expect(parseAssetCriticalityCsvRow(['host', 'host-1', 'low_impact'])).toEqual({
|
||||
valid: true,
|
||||
record: {
|
||||
idField: 'host.name',
|
||||
|
@ -143,9 +121,7 @@ describe('parseAssetCriticalityCsvRow', () => {
|
|||
});
|
||||
|
||||
it('should return the parsed row if criticality level is the wrong case', () => {
|
||||
expect(
|
||||
parseAssetCriticalityCsvRow(['host', 'host-1', 'LOW_IMPACT'], experimentalFeatures)
|
||||
).toEqual({
|
||||
expect(parseAssetCriticalityCsvRow(['host', 'host-1', 'LOW_IMPACT'])).toEqual({
|
||||
valid: true,
|
||||
record: {
|
||||
idField: 'host.name',
|
||||
|
@ -155,9 +131,7 @@ describe('parseAssetCriticalityCsvRow', () => {
|
|||
});
|
||||
});
|
||||
it('should return the parsed row if criticality level is UNASSIGNED', () => {
|
||||
expect(
|
||||
parseAssetCriticalityCsvRow(['host', 'host-1', 'UNASSIGNED'], experimentalFeatures)
|
||||
).toEqual({
|
||||
expect(parseAssetCriticalityCsvRow(['host', 'host-1', 'UNASSIGNED'])).toEqual({
|
||||
valid: true,
|
||||
record: {
|
||||
idField: 'host.name',
|
||||
|
|
|
@ -9,8 +9,7 @@ import type { CriticalityLevels } from './constants';
|
|||
import { ValidCriticalityLevels } from './constants';
|
||||
import { type AssetCriticalityUpsertForBulkUpload, type CriticalityLevel } from './types';
|
||||
import { EntityTypeToIdentifierField, type EntityType } from '../types';
|
||||
import { getAssetCriticalityEntityTypes } from './utils';
|
||||
import type { ExperimentalFeatures } from '../../experimental_features';
|
||||
import { getEntityAnalyticsEntityTypes } from '../utils';
|
||||
|
||||
const MAX_COLUMN_CHARS = 1000;
|
||||
|
||||
|
@ -40,10 +39,7 @@ const trimColumn = (column: string): string => {
|
|||
return column.length > MAX_COLUMN_CHARS ? `${column.substring(0, MAX_COLUMN_CHARS)}...` : column;
|
||||
};
|
||||
|
||||
export const parseAssetCriticalityCsvRow = (
|
||||
row: string[],
|
||||
experimentalFeatures: ExperimentalFeatures
|
||||
): ReturnType => {
|
||||
export const parseAssetCriticalityCsvRow = (row: string[]): ReturnType => {
|
||||
if (row.length !== 3) {
|
||||
return validationErrorWithMessage(
|
||||
i18n.translate('xpack.securitySolution.assetCriticality.csvUpload.expectedColumnsError', {
|
||||
|
@ -104,7 +100,7 @@ export const parseAssetCriticalityCsvRow = (
|
|||
);
|
||||
}
|
||||
|
||||
const enabledEntityTypes = getAssetCriticalityEntityTypes(experimentalFeatures);
|
||||
const enabledEntityTypes = getEntityAnalyticsEntityTypes();
|
||||
if (!enabledEntityTypes.includes(entityType as EntityType)) {
|
||||
return validationErrorWithMessage(
|
||||
i18n.translate('xpack.securitySolution.assetCriticality.csvUpload.invalidEntityTypeError', {
|
||||
|
|
|
@ -1,17 +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 { ExperimentalFeatures } from '../../experimental_features';
|
||||
import { getAllEntityTypes, getDisabledEntityTypes } from '../utils';
|
||||
|
||||
// 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));
|
||||
};
|
|
@ -1,17 +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 { ExperimentalFeatures } from '../../experimental_features';
|
||||
|
||||
import { getAllEntityTypes, getDisabledEntityTypes } from '../utils';
|
||||
|
||||
export const getEnabledStoreEntityTypes = (experimentalFeatures: ExperimentalFeatures) => {
|
||||
const allEntityTypes = getAllEntityTypes();
|
||||
const disabledEntityTypes = getDisabledEntityTypes(experimentalFeatures);
|
||||
|
||||
return allEntityTypes.filter((value) => !disabledEntityTypes.includes(value));
|
||||
};
|
|
@ -6,8 +6,6 @@
|
|||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
import { getAllEntityTypes, getDisabledEntityTypes } from '../utils';
|
||||
import type { ExperimentalFeatures } from '../../experimental_features';
|
||||
|
||||
/*
|
||||
* This utility function can be used to turn a TypeScript enum into a io-ts codec.
|
||||
|
@ -26,11 +24,3 @@ export function fromEnum<EnumType extends string>(
|
|||
t.identity
|
||||
);
|
||||
}
|
||||
|
||||
// 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));
|
||||
};
|
||||
|
|
|
@ -15,16 +15,19 @@ export enum EntityType {
|
|||
user = 'user',
|
||||
host = 'host',
|
||||
service = 'service',
|
||||
generic = 'generic',
|
||||
}
|
||||
|
||||
export enum EntityIdentifierFields {
|
||||
hostName = 'host.name',
|
||||
userName = 'user.name',
|
||||
serviceName = 'service.name',
|
||||
generic = 'entity.id',
|
||||
}
|
||||
|
||||
export const EntityTypeToIdentifierField: Record<EntityType, EntityIdentifierFields> = {
|
||||
[EntityType.host]: EntityIdentifierFields.hostName,
|
||||
[EntityType.user]: EntityIdentifierFields.userName,
|
||||
[EntityType.service]: EntityIdentifierFields.serviceName,
|
||||
[EntityType.generic]: EntityIdentifierFields.generic,
|
||||
};
|
||||
|
|
|
@ -4,14 +4,26 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { getAllEntityTypes } from './utils';
|
||||
import { getEnabledEntityTypes, getEntityAnalyticsEntityTypes } from './utils';
|
||||
import { EntityType } from './types';
|
||||
|
||||
describe('utils', () => {
|
||||
describe('getAllEntityTypes', () => {
|
||||
describe('getEntityAnalyticsEntityTypes', () => {
|
||||
it('should return all Entity Analytics entity types', () => {
|
||||
const entityTypes = getEntityAnalyticsEntityTypes();
|
||||
expect(entityTypes).toEqual([EntityType.user, EntityType.host, EntityType.service]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getEnabledEntityTypes', () => {
|
||||
it('should return all entity types', () => {
|
||||
const entityTypes = getAllEntityTypes();
|
||||
const entityTypes = getEnabledEntityTypes(true);
|
||||
expect(entityTypes).toEqual(Object.values(EntityType));
|
||||
});
|
||||
|
||||
it('should not return generic', () => {
|
||||
const entityTypes = getEnabledEntityTypes(false);
|
||||
expect(entityTypes).toEqual([EntityType.user, EntityType.host, EntityType.service]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,15 +4,22 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type { ExperimentalFeatures } from '../experimental_features';
|
||||
|
||||
import { EntityType } from './types';
|
||||
|
||||
export const getAllEntityTypes = (): EntityType[] => Object.values(EntityType);
|
||||
const ENTITY_ANALYTICS_ENTITY_TYPES = [EntityType.user, EntityType.host, EntityType.service];
|
||||
|
||||
export const getDisabledEntityTypes = (
|
||||
experimentalFeatures: ExperimentalFeatures
|
||||
): EntityType[] => {
|
||||
const disabledEntityTypes: EntityType[] = [];
|
||||
export const getEntityAnalyticsEntityTypes = (): EntityType[] => ENTITY_ANALYTICS_ENTITY_TYPES;
|
||||
|
||||
return disabledEntityTypes;
|
||||
export const getEnabledEntityTypes = (genericDefinitionEnabled: boolean): EntityType[] => {
|
||||
const entities = Object.values(EntityType);
|
||||
|
||||
if (genericDefinitionEnabled) {
|
||||
return entities;
|
||||
}
|
||||
|
||||
// Remove the index of generic
|
||||
entities.splice(entities.indexOf(EntityType.generic), 1);
|
||||
|
||||
return entities;
|
||||
};
|
||||
|
|
|
@ -93,10 +93,12 @@ export const EntityTypeToLevelField: Record<EntityType, RiskScoreFields> = {
|
|||
[EntityType.host]: RiskScoreFields.hostRisk,
|
||||
[EntityType.user]: RiskScoreFields.userRisk,
|
||||
[EntityType.service]: RiskScoreFields.serviceRisk,
|
||||
[EntityType.generic]: RiskScoreFields.unsupported, // We don't calculate risk for the generic entity
|
||||
};
|
||||
|
||||
export const EntityTypeToScoreField: Record<EntityType, RiskScoreFields> = {
|
||||
[EntityType.host]: RiskScoreFields.hostRiskScore,
|
||||
[EntityType.user]: RiskScoreFields.userRiskScore,
|
||||
[EntityType.service]: RiskScoreFields.serviceRiskScore,
|
||||
[EntityType.generic]: RiskScoreFields.unsupported, // We don't calculate risk for the generic entity
|
||||
};
|
||||
|
|
|
@ -1162,6 +1162,13 @@ components:
|
|||
- indexPattern
|
||||
- status
|
||||
- fieldHistoryLength
|
||||
EngineMetadata:
|
||||
type: object
|
||||
properties:
|
||||
Type:
|
||||
type: string
|
||||
required:
|
||||
- Type
|
||||
EngineStatus:
|
||||
enum:
|
||||
- installing
|
||||
|
@ -1175,6 +1182,7 @@ components:
|
|||
- $ref: '#/components/schemas/UserEntity'
|
||||
- $ref: '#/components/schemas/HostEntity'
|
||||
- $ref: '#/components/schemas/ServiceEntity'
|
||||
- $ref: '#/components/schemas/GenericEntity'
|
||||
EntityRiskLevels:
|
||||
enum:
|
||||
- Unknown
|
||||
|
@ -1269,7 +1277,42 @@ components:
|
|||
- user
|
||||
- host
|
||||
- service
|
||||
- generic
|
||||
type: string
|
||||
GenericEntity:
|
||||
type: object
|
||||
properties:
|
||||
'@timestamp':
|
||||
format: date-time
|
||||
type: string
|
||||
asset:
|
||||
type: object
|
||||
properties:
|
||||
criticality:
|
||||
$ref: '#/components/schemas/AssetCriticalityLevel'
|
||||
required:
|
||||
- criticality
|
||||
entity:
|
||||
type: object
|
||||
properties:
|
||||
category:
|
||||
type: string
|
||||
EngineMetadata:
|
||||
$ref: '#/components/schemas/EngineMetadata'
|
||||
id:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
- type
|
||||
required:
|
||||
- entity
|
||||
HostEntity:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -1286,13 +1329,18 @@ components:
|
|||
entity:
|
||||
type: object
|
||||
properties:
|
||||
EngineMetadata:
|
||||
$ref: '#/components/schemas/EngineMetadata'
|
||||
name:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
- type
|
||||
event:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -1344,6 +1392,7 @@ components:
|
|||
- host.name
|
||||
- user.name
|
||||
- service.name
|
||||
- entity.id
|
||||
type: string
|
||||
IndexPattern:
|
||||
type: string
|
||||
|
@ -1448,13 +1497,18 @@ components:
|
|||
entity:
|
||||
type: object
|
||||
properties:
|
||||
EngineMetadata:
|
||||
$ref: '#/components/schemas/EngineMetadata'
|
||||
name:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
- type
|
||||
event:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -1562,13 +1616,18 @@ components:
|
|||
entity:
|
||||
type: object
|
||||
properties:
|
||||
EngineMetadata:
|
||||
$ref: '#/components/schemas/EngineMetadata'
|
||||
name:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
- type
|
||||
event:
|
||||
type: object
|
||||
properties:
|
||||
|
|
|
@ -1162,6 +1162,13 @@ components:
|
|||
- indexPattern
|
||||
- status
|
||||
- fieldHistoryLength
|
||||
EngineMetadata:
|
||||
type: object
|
||||
properties:
|
||||
Type:
|
||||
type: string
|
||||
required:
|
||||
- Type
|
||||
EngineStatus:
|
||||
enum:
|
||||
- installing
|
||||
|
@ -1175,6 +1182,7 @@ components:
|
|||
- $ref: '#/components/schemas/UserEntity'
|
||||
- $ref: '#/components/schemas/HostEntity'
|
||||
- $ref: '#/components/schemas/ServiceEntity'
|
||||
- $ref: '#/components/schemas/GenericEntity'
|
||||
EntityRiskLevels:
|
||||
enum:
|
||||
- Unknown
|
||||
|
@ -1269,7 +1277,42 @@ components:
|
|||
- user
|
||||
- host
|
||||
- service
|
||||
- generic
|
||||
type: string
|
||||
GenericEntity:
|
||||
type: object
|
||||
properties:
|
||||
'@timestamp':
|
||||
format: date-time
|
||||
type: string
|
||||
asset:
|
||||
type: object
|
||||
properties:
|
||||
criticality:
|
||||
$ref: '#/components/schemas/AssetCriticalityLevel'
|
||||
required:
|
||||
- criticality
|
||||
entity:
|
||||
type: object
|
||||
properties:
|
||||
category:
|
||||
type: string
|
||||
EngineMetadata:
|
||||
$ref: '#/components/schemas/EngineMetadata'
|
||||
id:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
- type
|
||||
required:
|
||||
- entity
|
||||
HostEntity:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -1286,13 +1329,18 @@ components:
|
|||
entity:
|
||||
type: object
|
||||
properties:
|
||||
EngineMetadata:
|
||||
$ref: '#/components/schemas/EngineMetadata'
|
||||
name:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
- type
|
||||
event:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -1344,6 +1392,7 @@ components:
|
|||
- host.name
|
||||
- user.name
|
||||
- service.name
|
||||
- entity.id
|
||||
type: string
|
||||
IndexPattern:
|
||||
type: string
|
||||
|
@ -1448,13 +1497,18 @@ components:
|
|||
entity:
|
||||
type: object
|
||||
properties:
|
||||
EngineMetadata:
|
||||
$ref: '#/components/schemas/EngineMetadata'
|
||||
name:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
- type
|
||||
event:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -1562,13 +1616,18 @@ components:
|
|||
entity:
|
||||
type: object
|
||||
properties:
|
||||
EngineMetadata:
|
||||
$ref: '#/components/schemas/EngineMetadata'
|
||||
name:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- source
|
||||
- type
|
||||
event:
|
||||
type: object
|
||||
properties:
|
||||
|
|
|
@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { FormattedMessage, useI18n } from '@kbn/i18n-react';
|
||||
|
||||
import type { EntityType } from '../../../../../common/entity_analytics/types';
|
||||
import { useAssetCriticalityEntityTypes } from '../../../hooks/use_enabled_entity_types';
|
||||
import { useEntityAnalyticsTypes } from '../../../hooks/use_enabled_entity_types';
|
||||
import { EntityTypeToIdentifierField } from '../../../../../common/entity_analytics/types';
|
||||
|
||||
import {
|
||||
|
@ -61,7 +61,7 @@ export const AssetCriticalityFilePickerStep: React.FC<AssetCriticalityFilePicker
|
|||
line-height: ${useEuiFontSize('s').lineHeight};
|
||||
`;
|
||||
|
||||
const entityTypes = useAssetCriticalityEntityTypes();
|
||||
const entityTypes = useEntityAnalyticsTypes();
|
||||
|
||||
const sampleCSVContent = entityTypes
|
||||
.filter((entity): entity is SupportedEntityType => entity in entityCSVMap)
|
||||
|
|
|
@ -34,7 +34,7 @@ export const validateParsedContent = (
|
|||
errors: RowValidationErrors[];
|
||||
}>(
|
||||
(acc, row) => {
|
||||
const parsedRow = parseAssetCriticalityCsvRow(row, experimentalFeatures);
|
||||
const parsedRow = parseAssetCriticalityCsvRow(row);
|
||||
if (parsedRow.valid) {
|
||||
acc.valid.push(row);
|
||||
} else {
|
||||
|
|
|
@ -38,7 +38,7 @@ import {
|
|||
import type { Enablements } from './enablement_modal';
|
||||
import { EntityStoreEnablementModal } from './enablement_modal';
|
||||
import dashboardEnableImg from '../../../images/entity_store_dashboard.png';
|
||||
import { useStoreEntityTypes } from '../../../hooks/use_enabled_entity_types';
|
||||
import { useEntityStoreTypes } from '../../../hooks/use_enabled_entity_types';
|
||||
|
||||
interface EnableEntityStorePanelProps {
|
||||
state: {
|
||||
|
@ -51,7 +51,7 @@ export const EnablementPanel: React.FC<EnableEntityStorePanelProps> = ({ state }
|
|||
const riskEngineStatus = state.riskEngine.data?.risk_engine_status;
|
||||
const entityStoreStatus = state.entityStore.data?.status;
|
||||
const engines = state.entityStore.data?.engines;
|
||||
const enabledEntityTypes = useStoreEntityTypes();
|
||||
const enabledEntityTypes = useEntityStoreTypes();
|
||||
|
||||
const [modal, setModalState] = useState({ visible: false });
|
||||
const [riskEngineInitializing, setRiskEngineInitializing] = useState(false);
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
EuiFlexGroup,
|
||||
EuiPanel,
|
||||
} from '@elastic/eui';
|
||||
import { useStoreEntityTypes } from '../../../hooks/use_enabled_entity_types';
|
||||
import { useEntityAnalyticsTypes } from '../../../hooks/use_enabled_entity_types';
|
||||
import { RiskEngineStatusEnum } from '../../../../../common/api/entity_analytics';
|
||||
import { EntitiesList } from '../entities_list';
|
||||
import { useEntityStoreStatus } from '../hooks/use_entity_store';
|
||||
|
@ -25,7 +25,7 @@ import { EntityStoreErrorCallout } from './entity_store_error_callout';
|
|||
const EntityStoreDashboardPanelsComponent = () => {
|
||||
const riskEngineStatus = useRiskEngineStatus();
|
||||
const storeStatusQuery = useEntityStoreStatus({});
|
||||
const entityTypes = useStoreEntityTypes();
|
||||
const entityTypes = useEntityAnalyticsTypes();
|
||||
|
||||
const callouts = (storeStatusQuery.data?.engines ?? [])
|
||||
.filter((engine) => engine.status === 'error')
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useStoreEntityTypes } from '../../../../hooks/use_enabled_entity_types';
|
||||
import { useEntityStoreTypes } from '../../../../hooks/use_enabled_entity_types';
|
||||
import { useErrorToast } from '../../../../../common/hooks/use_error_toast';
|
||||
import { downloadBlob } from '../../../../../common/utils/download_blob';
|
||||
import { EngineComponentsStatusTable } from './components/engine_components_status';
|
||||
|
@ -42,7 +42,7 @@ export const EngineStatus: React.FC = () => {
|
|||
isLoading: isStatusAPILoading,
|
||||
error,
|
||||
} = useEntityStoreStatus({ withComponents: true });
|
||||
const enabledEntityTypes = useStoreEntityTypes();
|
||||
const enabledEntityTypes = useEntityStoreTypes();
|
||||
|
||||
const downloadJson = () => {
|
||||
downloadBlob(new Blob([JSON.stringify(data)]), FILE_NAME);
|
||||
|
|
|
@ -36,6 +36,7 @@ const responseData: ListEntitiesResponse = {
|
|||
entity: {
|
||||
name: `Entity Name ${index}`,
|
||||
source: 'test-index',
|
||||
type: 'user',
|
||||
},
|
||||
}),
|
||||
10
|
||||
|
|
|
@ -26,7 +26,7 @@ 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';
|
||||
import { useStoreEntityTypes } from '../../hooks/use_enabled_entity_types';
|
||||
import { useEntityStoreTypes } from '../../hooks/use_enabled_entity_types';
|
||||
|
||||
export const EntitiesList: React.FC = () => {
|
||||
const { deleteQuery, setQuery, isInitializing, from, to } = useGlobalTime();
|
||||
|
@ -37,7 +37,7 @@ export const EntitiesList: React.FC = () => {
|
|||
field: '@timestamp',
|
||||
direction: Direction.desc,
|
||||
});
|
||||
const entityTypes = useStoreEntityTypes();
|
||||
const entityTypes = useEntityStoreTypes();
|
||||
const [selectedSeverities, setSelectedSeverities] = useState<RiskSeverity[]>([]);
|
||||
const [selectedCriticalities, setSelectedCriticalities] = useState<CriticalityLevels[]>([]);
|
||||
const [selectedSources, setSelectedSources] = useState<EntitySourceTag[]>([]);
|
||||
|
|
|
@ -13,6 +13,7 @@ import type {
|
|||
HostEntity,
|
||||
ServiceEntity,
|
||||
UserEntity,
|
||||
GenericEntity,
|
||||
} from '../../../../common/api/entity_analytics';
|
||||
|
||||
describe('helpers', () => {
|
||||
|
@ -26,6 +27,26 @@ describe('helpers', () => {
|
|||
entity: {
|
||||
name: 'test_user',
|
||||
source: 'logs-test',
|
||||
type: 'AWS IAM User',
|
||||
EngineMetadata: {
|
||||
Type: 'user',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(getEntityType(userEntity)).toBe('user');
|
||||
});
|
||||
|
||||
it('should return "user" if the record is a UserEntity (with user.entity)', () => {
|
||||
const userEntity: UserEntity = {
|
||||
'@timestamp': '2021-08-02T14:00:00.000Z',
|
||||
user: {
|
||||
name: 'test_user',
|
||||
},
|
||||
entity: {
|
||||
name: 'test_user',
|
||||
source: 'logs-test',
|
||||
type: 'user',
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -41,11 +62,32 @@ describe('helpers', () => {
|
|||
entity: {
|
||||
name: 'test_host',
|
||||
source: 'logs-test',
|
||||
type: 'EC2 Instance',
|
||||
EngineMetadata: {
|
||||
Type: 'host',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(getEntityType(hostEntity)).toBe('host');
|
||||
});
|
||||
|
||||
it('should return "host" if the record is a HostEntity (with entity.type)', () => {
|
||||
const hostEntity: HostEntity = {
|
||||
'@timestamp': '2021-08-02T14:00:00.000Z',
|
||||
host: {
|
||||
name: 'test_host',
|
||||
},
|
||||
entity: {
|
||||
name: 'test_host',
|
||||
source: 'logs-test',
|
||||
type: 'host',
|
||||
},
|
||||
};
|
||||
|
||||
expect(getEntityType(hostEntity)).toBe('host');
|
||||
});
|
||||
|
||||
it('should return "service" if the record is a ServiceEntity', () => {
|
||||
const serviceEntity: ServiceEntity = {
|
||||
'@timestamp': '2021-08-02T14:00:00.000Z',
|
||||
|
@ -55,12 +97,63 @@ describe('helpers', () => {
|
|||
entity: {
|
||||
name: 'test_service',
|
||||
source: 'logs-test',
|
||||
type: 'SaaS',
|
||||
EngineMetadata: {
|
||||
Type: 'service',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(getEntityType(serviceEntity)).toBe('service');
|
||||
});
|
||||
|
||||
it('should return "service" if the record is a ServiceEntity (with entity.type)', () => {
|
||||
const serviceEntity: ServiceEntity = {
|
||||
'@timestamp': '2021-08-02T14:00:00.000Z',
|
||||
service: {
|
||||
name: 'test_service',
|
||||
},
|
||||
entity: {
|
||||
name: 'test_service',
|
||||
source: 'logs-test',
|
||||
type: 'service',
|
||||
},
|
||||
};
|
||||
|
||||
expect(getEntityType(serviceEntity)).toBe('service');
|
||||
});
|
||||
|
||||
it('should return "generic" if the record is a ServiceEntity', () => {
|
||||
const genericEntity: GenericEntity = {
|
||||
'@timestamp': '2021-08-02T14:00:00.000Z',
|
||||
entity: {
|
||||
id: 'arn',
|
||||
name: 'test_generic',
|
||||
source: 'logs-test',
|
||||
type: 'PostgreSQL Database',
|
||||
EngineMetadata: {
|
||||
Type: 'generic',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(getEntityType(genericEntity)).toBe('generic');
|
||||
});
|
||||
|
||||
it('should return "generic" if the record is a ServiceEntity (with entity.type)', () => {
|
||||
const genericEntity: GenericEntity = {
|
||||
'@timestamp': '2021-08-02T14:00:00.000Z',
|
||||
entity: {
|
||||
id: 'arn',
|
||||
name: 'test_generic',
|
||||
source: 'logs-test',
|
||||
type: 'generic',
|
||||
},
|
||||
};
|
||||
|
||||
expect(getEntityType(genericEntity)).toBe('generic');
|
||||
});
|
||||
|
||||
it('should throw an error if the record does not match any entity type', () => {
|
||||
const unknownEntity = {
|
||||
'@timestamp': '2021-08-02T14:00:00.000Z',
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
import React from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import type { IconType } from '@elastic/eui';
|
||||
import { get } from 'lodash/fp';
|
||||
import { getAllEntityTypes } from '../../../../common/entity_analytics/utils';
|
||||
import { EntityType } from '../../../../common/entity_analytics/types';
|
||||
|
||||
import {
|
||||
|
@ -18,23 +16,21 @@ import {
|
|||
import type { Entity } from '../../../../common/api/entity_analytics/entity_store/entities/common.gen';
|
||||
|
||||
export const getEntityType = (record: Entity): EntityType => {
|
||||
const allEntityTypes = getAllEntityTypes();
|
||||
// Looking at `entity.type` to keep backward compatibility
|
||||
const entityType = record.entity.EngineMetadata?.Type || record.entity.type;
|
||||
|
||||
const entityType = allEntityTypes.find((type) => {
|
||||
return get(type, record) !== undefined;
|
||||
});
|
||||
|
||||
if (!entityType) {
|
||||
if (Object.keys(EntityType).indexOf(entityType) < 0) {
|
||||
throw new Error(`Unexpected entity: ${JSON.stringify(record)}`);
|
||||
}
|
||||
|
||||
return entityType;
|
||||
return EntityType[entityType as keyof typeof EntityType];
|
||||
};
|
||||
|
||||
export const EntityIconByType: Record<EntityType, IconType> = {
|
||||
[EntityType.user]: 'user',
|
||||
[EntityType.host]: 'storage',
|
||||
[EntityType.service]: 'node',
|
||||
[EntityType.generic]: 'globe',
|
||||
};
|
||||
|
||||
export const sourceFieldToText = (source: string) => {
|
||||
|
|
|
@ -18,6 +18,17 @@ const mockedExperimentalFeatures = mockGlobalState.app.enableExperimental;
|
|||
jest.mock('../../../../common/hooks/use_experimental_features', () => ({
|
||||
useEnableExperimental: () => ({ ...mockedExperimentalFeatures }),
|
||||
}));
|
||||
|
||||
const mockUseUiSettings = jest.fn().mockReturnValue([true]);
|
||||
jest.mock('@kbn/kibana-react-plugin/public', () => {
|
||||
const original = jest.requireActual('@kbn/kibana-react-plugin/public');
|
||||
|
||||
return {
|
||||
...original,
|
||||
useUiSetting$: () => mockUseUiSettings(),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../../common/hooks/use_global_filter_query');
|
||||
|
||||
describe('useEntitiesListFilters', () => {
|
||||
|
|
|
@ -12,10 +12,14 @@ import {
|
|||
RISK_SCORE_INDEX_PATTERN,
|
||||
type CriticalityLevels,
|
||||
} from '../../../../../common/constants';
|
||||
import { EntityTypeToLevelField, type RiskSeverity } from '../../../../../common/search_strategy';
|
||||
import {
|
||||
EntityTypeToLevelField,
|
||||
RiskScoreFields,
|
||||
type RiskSeverity,
|
||||
} from '../../../../../common/search_strategy';
|
||||
import { useGlobalFilterQuery } from '../../../../common/hooks/use_global_filter_query';
|
||||
import { EntitySourceTag } from '../types';
|
||||
import { useStoreEntityTypes } from '../../../hooks/use_enabled_entity_types';
|
||||
import { useEntityStoreTypes } from '../../../hooks/use_enabled_entity_types';
|
||||
|
||||
interface UseEntitiesListFiltersParams {
|
||||
selectedSeverities: RiskSeverity[];
|
||||
|
@ -29,7 +33,7 @@ export const useEntitiesListFilters = ({
|
|||
selectedSources,
|
||||
}: UseEntitiesListFiltersParams) => {
|
||||
const { filterQuery: globalQuery } = useGlobalFilterQuery();
|
||||
const enabledEntityTypes = useStoreEntityTypes();
|
||||
const enabledEntityTypes = useEntityStoreTypes();
|
||||
|
||||
return useMemo(() => {
|
||||
const criticalityFilter: QueryDslQueryContainer[] = selectedCriticalities.length
|
||||
|
@ -60,11 +64,15 @@ export const useEntitiesListFilters = ({
|
|||
? [
|
||||
{
|
||||
bool: {
|
||||
should: enabledEntityTypes.map((type) => ({
|
||||
terms: {
|
||||
[EntityTypeToLevelField[type]]: selectedSeverities,
|
||||
},
|
||||
})),
|
||||
should: enabledEntityTypes
|
||||
.filter((type) => {
|
||||
return EntityTypeToLevelField[type] !== RiskScoreFields.unsupported;
|
||||
})
|
||||
.map((type) => ({
|
||||
terms: {
|
||||
[EntityTypeToLevelField[type]]: selectedSeverities,
|
||||
},
|
||||
})),
|
||||
},
|
||||
},
|
||||
]
|
||||
|
|
|
@ -34,7 +34,7 @@ import { useSourcererDataView } from '../../sourcerer/containers';
|
|||
import type { RiskEngineMissingPrivilegesResponse } from '../hooks/use_missing_risk_engine_privileges';
|
||||
import { userHasRiskEngineReadPermissions } from '../common';
|
||||
import { EntityIconByType } from './entity_store/helpers';
|
||||
import { useRiskEngineEntityTypes } from '../hooks/use_enabled_entity_types';
|
||||
import { useEntityAnalyticsTypes } from '../hooks/use_enabled_entity_types';
|
||||
interface IRiskScorePreviewPanel {
|
||||
showMessage: React.ReactNode;
|
||||
hideMessage: React.ReactNode;
|
||||
|
@ -142,7 +142,7 @@ const RiskEnginePreview: React.FC<{ includeClosedAlerts: boolean; from: string;
|
|||
from,
|
||||
to,
|
||||
}) => {
|
||||
const entityTypes = useRiskEngineEntityTypes();
|
||||
const entityTypes = useEntityAnalyticsTypes();
|
||||
|
||||
const [filters] = useState<{ bool: BoolQuery }>({
|
||||
bool: { must: [], filter: [], should: [], must_not: [] },
|
||||
|
|
|
@ -6,28 +6,24 @@
|
|||
*/
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { getAssetCriticalityEntityTypes } from '../../../common/entity_analytics/asset_criticality/utils';
|
||||
import { getRiskEngineEntityTypes } from '../../../common/entity_analytics/risk_engine/utils';
|
||||
import { getEnabledStoreEntityTypes } from '../../../common/entity_analytics/entity_store/utils';
|
||||
import { useEnableExperimental } from '../../common/hooks/use_experimental_features';
|
||||
import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
|
||||
import { SECURITY_SOLUTION_ENABLE_ASSET_INVENTORY_SETTING } from '@kbn/management-settings-ids';
|
||||
import {
|
||||
getEntityAnalyticsEntityTypes,
|
||||
getEnabledEntityTypes,
|
||||
} from '../../../common/entity_analytics/utils';
|
||||
|
||||
export const useStoreEntityTypes = () => {
|
||||
const experimentalFeatures = useEnableExperimental();
|
||||
|
||||
return useMemo(() => getEnabledStoreEntityTypes(experimentalFeatures), [experimentalFeatures]);
|
||||
};
|
||||
|
||||
export const useRiskEngineEntityTypes = () => {
|
||||
const experimentalFeatures = useEnableExperimental();
|
||||
|
||||
return useMemo(() => getRiskEngineEntityTypes(experimentalFeatures), [experimentalFeatures]);
|
||||
};
|
||||
|
||||
export const useAssetCriticalityEntityTypes = () => {
|
||||
const experimentalFeatures = useEnableExperimental();
|
||||
export const useEntityStoreTypes = () => {
|
||||
const [genericEntityStoreEnabled] = useUiSetting$<boolean>(
|
||||
SECURITY_SOLUTION_ENABLE_ASSET_INVENTORY_SETTING
|
||||
);
|
||||
|
||||
return useMemo(
|
||||
() => getAssetCriticalityEntityTypes(experimentalFeatures),
|
||||
[experimentalFeatures]
|
||||
() => getEnabledEntityTypes(genericEntityStoreEnabled),
|
||||
[genericEntityStoreEnabled]
|
||||
);
|
||||
};
|
||||
|
||||
export const useEntityAnalyticsTypes = () => {
|
||||
return useMemo(() => getEntityAnalyticsEntityTypes(), []);
|
||||
};
|
||||
|
|
|
@ -22,7 +22,7 @@ import { EntityAnalyticsAnomalies } from '../components/entity_analytics_anomali
|
|||
import { EntityStoreDashboardPanels } from '../components/entity_store/components/dashboard_entity_store_panels';
|
||||
import { EntityAnalyticsRiskScores } from '../components/entity_analytics_risk_score';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features';
|
||||
import { useStoreEntityTypes } from '../hooks/use_enabled_entity_types';
|
||||
import { useEntityAnalyticsTypes } from '../hooks/use_enabled_entity_types';
|
||||
|
||||
const EntityAnalyticsComponent = () => {
|
||||
const [skipEmptyPrompt, setSkipEmptyPrompt] = React.useState(false);
|
||||
|
@ -30,7 +30,7 @@ const EntityAnalyticsComponent = () => {
|
|||
const { indicesExist, loading: isSourcererLoading, sourcererDataView } = useSourcererDataView();
|
||||
const isEntityStoreFeatureFlagDisabled = useIsExperimentalFeatureEnabled('entityStoreDisabled');
|
||||
const showEmptyPrompt = !indicesExist && !skipEmptyPrompt;
|
||||
const entityTypes = useStoreEntityTypes();
|
||||
const entityTypes = useEntityAnalyticsTypes();
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -49,7 +49,7 @@ import {
|
|||
import { useEntityEnginePrivileges } from '../components/entity_store/hooks/use_entity_engine_privileges';
|
||||
import { MissingPrivilegesCallout } from '../components/entity_store/components/missing_privileges_callout';
|
||||
import { EngineStatus } from '../components/entity_store/components/engines_status';
|
||||
import { useStoreEntityTypes } from '../hooks/use_enabled_entity_types';
|
||||
import { useEntityStoreTypes } from '../hooks/use_enabled_entity_types';
|
||||
import { EntityStoreErrorCallout } from '../components/entity_store/components/entity_store_error_callout';
|
||||
|
||||
enum TabId {
|
||||
|
@ -82,7 +82,7 @@ export const EntityStoreManagementPage = () => {
|
|||
const hasAssetCriticalityWritePermissions = assetCriticalityPrivileges?.has_write_permissions;
|
||||
const [selectedTabId, setSelectedTabId] = useState(TabId.Import);
|
||||
const entityStoreStatus = useEntityStoreStatus({});
|
||||
const entityTypes = useStoreEntityTypes();
|
||||
const entityTypes = useEntityStoreTypes();
|
||||
const enableStoreMutation = useEnableEntityStoreMutation();
|
||||
const stopEntityEngineMutation = useStopEntityEngineMutation(entityTypes);
|
||||
const deleteEntityEngineMutation = useDeleteEntityEngineMutation({
|
||||
|
|
|
@ -35,6 +35,7 @@ export const EntityPanelKeyByType: Record<EntityType, string | undefined> = {
|
|||
[EntityType.host]: HostPanelKey,
|
||||
[EntityType.user]: UserPanelKey,
|
||||
[EntityType.service]: ServicePanelKey,
|
||||
[EntityType.generic]: undefined, // TODO create generic flyout?
|
||||
};
|
||||
|
||||
// TODO rename all params and merged them as 'entityName'
|
||||
|
@ -42,4 +43,5 @@ export const EntityPanelParamByType: Record<EntityType, string | undefined> = {
|
|||
[EntityType.host]: 'hostName',
|
||||
[EntityType.user]: 'userName',
|
||||
[EntityType.service]: 'serviceName',
|
||||
[EntityType.generic]: undefined, // TODO create generic flyout?
|
||||
};
|
||||
|
|
|
@ -82,6 +82,7 @@ const entityTypeByIdField = {
|
|||
'host.name': 'host',
|
||||
'user.name': 'user',
|
||||
'service.name': 'service',
|
||||
'entity.id': 'generic',
|
||||
} as const;
|
||||
|
||||
export const getImplicitEntityFields = (record: AssetCriticalityUpsertWithDeleted) => {
|
||||
|
|
|
@ -28,7 +28,7 @@ class TransformCSVToUpsertRecords extends Transform {
|
|||
callback: (error: Error | null, data?: AssetCriticalityUpsertForBulkUpload | Error) => void
|
||||
) {
|
||||
try {
|
||||
const parseResult = parseAssetCriticalityCsvRow(chunk, this.experimentalFeatures);
|
||||
const parseResult = parseAssetCriticalityCsvRow(chunk);
|
||||
if (isErrorResult(parseResult)) {
|
||||
return callback(null, new Error(parseResult.error));
|
||||
} else {
|
||||
|
|
|
@ -66,6 +66,7 @@ const buildIngestPipeline = ({
|
|||
{
|
||||
set: {
|
||||
field: 'entity.name',
|
||||
override: false,
|
||||
value: `{{${description.identityField}}}`,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -10,12 +10,14 @@ import {
|
|||
HOST_DEFINITION_VERSION,
|
||||
USER_DEFINITION_VERSION,
|
||||
SERVICE_DEFINITION_VERSION,
|
||||
GENERIC_DEFINITION_VERSION,
|
||||
} from './entity_descriptions';
|
||||
|
||||
export const VERSIONS_BY_ENTITY_TYPE: Record<EntityType, string> = {
|
||||
host: HOST_DEFINITION_VERSION,
|
||||
user: USER_DEFINITION_VERSION,
|
||||
service: SERVICE_DEFINITION_VERSION,
|
||||
generic: GENERIC_DEFINITION_VERSION,
|
||||
};
|
||||
|
||||
export const DEFAULT_FIELD_HISTORY_LENGTH = 10;
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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 { newestValue } from './field_utils';
|
||||
import type { EntityDescription } from '../types';
|
||||
import { getCommonFieldDescriptions } from './common';
|
||||
|
||||
export const GENERIC_DEFINITION_VERSION = '1.0.0';
|
||||
export const GENERIC_IDENTITY_FIELD = 'entity.id';
|
||||
export const genericEntityEngineDescription: EntityDescription = {
|
||||
entityType: 'generic',
|
||||
version: GENERIC_DEFINITION_VERSION,
|
||||
identityField: GENERIC_IDENTITY_FIELD,
|
||||
settings: {
|
||||
timestampField: '@timestamp',
|
||||
},
|
||||
fields: [
|
||||
newestValue({ source: 'entity.name' }),
|
||||
newestValue({ source: 'entity.source' }),
|
||||
newestValue({ source: 'entity.type' }),
|
||||
newestValue({ source: 'entity.sub_type' }),
|
||||
newestValue({ source: 'entity.url' }),
|
||||
|
||||
newestValue({ source: 'cloud.account.id' }),
|
||||
newestValue({ source: 'cloud.account.name' }),
|
||||
newestValue({ source: 'cloud.availability_zone' }),
|
||||
newestValue({ source: 'cloud.instance.id' }),
|
||||
newestValue({ source: 'cloud.instance.name' }),
|
||||
newestValue({ source: 'cloud.machine.type' }),
|
||||
newestValue({ source: 'cloud.project.id' }),
|
||||
newestValue({ source: 'cloud.project.name' }),
|
||||
newestValue({ source: 'cloud.provider' }),
|
||||
newestValue({ source: 'cloud.region' }),
|
||||
newestValue({ source: 'cloud.service.name' }),
|
||||
|
||||
newestValue({ source: 'host.architecture' }),
|
||||
newestValue({ source: 'host.boot.id' }),
|
||||
newestValue({ source: 'host.cpu.usage' }),
|
||||
newestValue({ source: 'host.disk.read.bytes' }),
|
||||
newestValue({ source: 'host.disk.write.bytes' }),
|
||||
newestValue({ source: 'host.domain' }),
|
||||
newestValue({ source: 'host.hostname' }),
|
||||
newestValue({ source: 'host.id' }),
|
||||
newestValue({ source: 'host.mac' }),
|
||||
newestValue({ source: 'host.name' }),
|
||||
newestValue({ source: 'host.network.egress.bytes' }),
|
||||
newestValue({ source: 'host.network.egress.packets' }),
|
||||
newestValue({ source: 'host.network.ingress.bytes' }),
|
||||
newestValue({ source: 'host.network.ingress.packets' }),
|
||||
newestValue({ source: 'host.pid_ns_ino' }),
|
||||
newestValue({ source: 'host.type' }),
|
||||
newestValue({ source: 'host.uptime' }),
|
||||
newestValue({
|
||||
source: 'host.ip',
|
||||
mapping: {
|
||||
type: 'ip',
|
||||
},
|
||||
}),
|
||||
|
||||
newestValue({ source: 'user.domain' }),
|
||||
newestValue({ source: 'user.email' }),
|
||||
newestValue({ source: 'user.full_name' }),
|
||||
newestValue({ source: 'user.roles' }),
|
||||
newestValue({ source: 'user.hash' }),
|
||||
newestValue({ source: 'user.id' }),
|
||||
newestValue({
|
||||
source: 'user.name',
|
||||
mapping: {
|
||||
type: 'keyword',
|
||||
fields: {
|
||||
text: {
|
||||
type: 'match_only_text',
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
newestValue({
|
||||
source: 'user.full_name',
|
||||
mapping: {
|
||||
type: 'keyword',
|
||||
fields: {
|
||||
text: {
|
||||
type: 'match_only_text',
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
newestValue({ source: 'orchestrator.api_version' }),
|
||||
newestValue({ source: 'orchestrator.cluster.id' }),
|
||||
newestValue({ source: 'orchestrator.cluster.name' }),
|
||||
newestValue({ source: 'orchestrator.cluster.url' }),
|
||||
newestValue({ source: 'orchestrator.cluster.version' }),
|
||||
newestValue({ source: 'orchestrator.namespace' }),
|
||||
newestValue({ source: 'orchestrator.organization' }),
|
||||
newestValue({ source: 'orchestrator.resource.annotation' }),
|
||||
newestValue({ source: 'orchestrator.resource.id' }),
|
||||
newestValue({ source: 'orchestrator.resource.ip' }),
|
||||
newestValue({ source: 'orchestrator.resource.label' }),
|
||||
newestValue({ source: 'orchestrator.resource.name' }),
|
||||
newestValue({ source: 'orchestrator.resource.parent.type' }),
|
||||
newestValue({ source: 'orchestrator.resource.type' }),
|
||||
newestValue({ source: 'orchestrator.type' }),
|
||||
|
||||
...getCommonFieldDescriptions('generic'),
|
||||
],
|
||||
};
|
|
@ -8,4 +8,6 @@
|
|||
export * from './host';
|
||||
export * from './user';
|
||||
export * from './service';
|
||||
export * from './generic';
|
||||
|
||||
export { getCommonFieldDescriptions } from './common';
|
||||
|
|
|
@ -26,7 +26,7 @@ import type {
|
|||
import type { TaskManagerStartContract } from '@kbn/task-manager-plugin/server';
|
||||
import { defaultOptions } from './constants';
|
||||
import type { SecurityPluginStart } from '@kbn/security-plugin/server';
|
||||
import type { KibanaRequest } from '@kbn/core/server';
|
||||
import type { IUiSettingsClient, KibanaRequest } from '@kbn/core/server';
|
||||
import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks';
|
||||
import { createStubDataView } from '@kbn/data-views-plugin/common/mocks';
|
||||
|
||||
|
@ -117,6 +117,11 @@ describe('EntityStoreDataClient', () => {
|
|||
const clusterClientMock = elasticsearchServiceMock.createScopedClusterClient();
|
||||
const esClientMock = clusterClientMock.asCurrentUser;
|
||||
const loggerMock = loggingSystemMock.createLogger();
|
||||
const uiSettingsClientGetMock = jest.fn();
|
||||
const uiSettingsClientMock = {
|
||||
get: uiSettingsClientGetMock,
|
||||
} as unknown as IUiSettingsClient;
|
||||
|
||||
const dataClient = new EntityStoreDataClient({
|
||||
clusterClient: clusterClientMock,
|
||||
logger: loggerMock,
|
||||
|
@ -137,6 +142,7 @@ describe('EntityStoreDataClient', () => {
|
|||
},
|
||||
} as unknown as SecurityPluginStart,
|
||||
request: {} as KibanaRequest,
|
||||
uiSettingsClient: uiSettingsClientMock,
|
||||
});
|
||||
|
||||
const defaultSearchParams = {
|
||||
|
@ -438,6 +444,47 @@ describe('EntityStoreDataClient', () => {
|
|||
|
||||
expect(spyInit).toHaveBeenCalledWith(EntityType.host, expect.anything(), expect.anything());
|
||||
});
|
||||
|
||||
it('enable all', async () => {
|
||||
uiSettingsClientGetMock.mockReturnValue(true);
|
||||
await dataClient.enable({
|
||||
...defaultOptions,
|
||||
});
|
||||
|
||||
expect(spyInit).toHaveBeenCalledWith(EntityType.host, expect.anything(), expect.anything());
|
||||
expect(spyInit).toHaveBeenCalledWith(EntityType.user, expect.anything(), expect.anything());
|
||||
expect(spyInit).toHaveBeenCalledWith(
|
||||
EntityType.service,
|
||||
expect.anything(),
|
||||
expect.anything()
|
||||
);
|
||||
expect(spyInit).toHaveBeenCalledWith(
|
||||
EntityType.generic,
|
||||
expect.anything(),
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
|
||||
it('enable all without generic', async () => {
|
||||
uiSettingsClientGetMock.mockReturnValue(false);
|
||||
|
||||
await dataClient.enable({
|
||||
...defaultOptions,
|
||||
});
|
||||
|
||||
expect(spyInit).toHaveBeenCalledWith(EntityType.host, expect.anything(), expect.anything());
|
||||
expect(spyInit).toHaveBeenCalledWith(EntityType.user, expect.anything(), expect.anything());
|
||||
expect(spyInit).toHaveBeenCalledWith(
|
||||
EntityType.service,
|
||||
expect.anything(),
|
||||
expect.anything()
|
||||
);
|
||||
expect(spyInit).not.toHaveBeenCalledWith(
|
||||
EntityType.generic,
|
||||
expect.anything(),
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('applyDataViewIndices', () => {
|
||||
|
|
|
@ -12,6 +12,7 @@ import type {
|
|||
AuditLogger,
|
||||
IScopedClusterClient,
|
||||
AuditEvent,
|
||||
IUiSettingsClient,
|
||||
AnalyticsServiceSetup,
|
||||
KibanaRequest,
|
||||
} from '@kbn/core/server';
|
||||
|
@ -26,18 +27,19 @@ import type { EntityDefinitionWithState } from '@kbn/entityManager-plugin/server
|
|||
import type { EntityDefinition } from '@kbn/entities-schema';
|
||||
import type { estypes } from '@elastic/elasticsearch';
|
||||
import { SO_ENTITY_DEFINITION_TYPE } from '@kbn/entityManager-plugin/server/saved_objects';
|
||||
import { SECURITY_SOLUTION_ENABLE_ASSET_INVENTORY_SETTING } from '@kbn/management-settings-ids';
|
||||
import { RISK_SCORE_INDEX_PATTERN } from '../../../../common/constants';
|
||||
import {
|
||||
ENTITY_STORE_INDEX_PATTERN,
|
||||
ENTITY_STORE_REQUIRED_ES_CLUSTER_PRIVILEGES,
|
||||
ENTITY_STORE_SOURCE_REQUIRED_ES_INDEX_PRIVILEGES,
|
||||
} from '../../../../common/entity_analytics/entity_store/constants';
|
||||
import { getEnabledEntityTypes } from '../../../../common/entity_analytics/utils';
|
||||
import {
|
||||
getAllMissingPrivileges,
|
||||
getMissingPrivilegesErrorMessage,
|
||||
} from '../../../../common/entity_analytics/privileges';
|
||||
import { merge } from '../../../../common/utils/objects/merge';
|
||||
import { getEnabledStoreEntityTypes } from '../../../../common/entity_analytics/entity_store/utils';
|
||||
import { EntityType } from '../../../../common/entity_analytics/types';
|
||||
import type { ExperimentalFeatures } from '../../../../common';
|
||||
import type {
|
||||
|
@ -143,6 +145,7 @@ interface EntityStoreClientOpts {
|
|||
apiKeyManager?: ApiKeyManager;
|
||||
security: SecurityPluginStart;
|
||||
request: KibanaRequest;
|
||||
uiSettingsClient: IUiSettingsClient;
|
||||
}
|
||||
|
||||
interface SearchEntitiesParams {
|
||||
|
@ -161,6 +164,7 @@ export class EntityStoreDataClient {
|
|||
private riskScoreDataClient: RiskScoreDataClient;
|
||||
private esClient: ElasticsearchClient;
|
||||
private apiKeyGenerator?: ApiKeyManager;
|
||||
private uiSettingsClient: IUiSettingsClient;
|
||||
|
||||
constructor(private readonly options: EntityStoreClientOpts) {
|
||||
const {
|
||||
|
@ -171,9 +175,11 @@ export class EntityStoreDataClient {
|
|||
kibanaVersion,
|
||||
namespace,
|
||||
apiKeyManager,
|
||||
uiSettingsClient,
|
||||
} = options;
|
||||
this.esClient = clusterClient.asCurrentUser;
|
||||
this.apiKeyGenerator = apiKeyManager;
|
||||
this.uiSettingsClient = uiSettingsClient;
|
||||
|
||||
this.entityClient = new EntityClient({
|
||||
clusterClient,
|
||||
|
@ -252,8 +258,7 @@ export class EntityStoreDataClient {
|
|||
const run = <T>(fn: () => Promise<T>) =>
|
||||
new Promise<T>((resolve) => setTimeout(() => fn().then(resolve), 0));
|
||||
|
||||
const { experimentalFeatures } = this.options;
|
||||
const enabledEntityTypes = getEnabledStoreEntityTypes(experimentalFeatures);
|
||||
const enabledEntityTypes = await this.getEnabledEntityTypes();
|
||||
|
||||
// When entityTypes param is defined it only enables the engines that are provided
|
||||
const enginesTypes = requestBodyOverrides.entityTypes
|
||||
|
@ -270,26 +275,39 @@ export class EntityStoreDataClient {
|
|||
return { engines, succeeded: true };
|
||||
}
|
||||
|
||||
private async getEnabledEntityTypes(): Promise<EntityType[]> {
|
||||
const genericEntityStoreEnabled = await this.uiSettingsClient.get<boolean>(
|
||||
SECURITY_SOLUTION_ENABLE_ASSET_INVENTORY_SETTING
|
||||
);
|
||||
|
||||
return getEnabledEntityTypes(genericEntityStoreEnabled);
|
||||
}
|
||||
|
||||
public async status({
|
||||
include_components: withComponents = false,
|
||||
}: GetEntityStoreStatusRequestQuery): Promise<GetEntityStoreStatusResponse> {
|
||||
const { namespace } = this.options;
|
||||
const { engines, count } = await this.engineClient.list();
|
||||
const { engines } = await this.engineClient.list();
|
||||
|
||||
const enabledEntityTypes = await this.getEnabledEntityTypes();
|
||||
const enabledEngines = engines.filter((engine) => {
|
||||
return enabledEntityTypes.indexOf(EntityType[engine.type]) > -1;
|
||||
});
|
||||
|
||||
let status = ENTITY_STORE_STATUS.RUNNING;
|
||||
if (count === 0) {
|
||||
if (enabledEngines.length === 0) {
|
||||
status = ENTITY_STORE_STATUS.NOT_INSTALLED;
|
||||
} else if (engines.some((engine) => engine.status === ENGINE_STATUS.ERROR)) {
|
||||
} else if (enabledEngines.some((engine) => engine.status === ENGINE_STATUS.ERROR)) {
|
||||
status = ENTITY_STORE_STATUS.ERROR;
|
||||
} else if (engines.every((engine) => engine.status === ENGINE_STATUS.STOPPED)) {
|
||||
} else if (enabledEngines.every((engine) => engine.status === ENGINE_STATUS.STOPPED)) {
|
||||
status = ENTITY_STORE_STATUS.STOPPED;
|
||||
} else if (engines.some((engine) => engine.status === ENGINE_STATUS.INSTALLING)) {
|
||||
} else if (enabledEngines.some((engine) => engine.status === ENGINE_STATUS.INSTALLING)) {
|
||||
status = ENTITY_STORE_STATUS.INSTALLING;
|
||||
}
|
||||
|
||||
if (withComponents) {
|
||||
const enginesWithComponents = await Promise.all(
|
||||
engines.map(async (engine) => {
|
||||
enabledEngines.map(async (engine) => {
|
||||
const id = buildEntityDefinitionId(engine.type, namespace);
|
||||
const {
|
||||
definitions: [definition],
|
||||
|
@ -314,7 +332,7 @@ export class EntityStoreDataClient {
|
|||
|
||||
return { engines: enginesWithComponents, status };
|
||||
} else {
|
||||
return { engines, status };
|
||||
return { engines: enabledEngines, status };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
hostEntityEngineDescription,
|
||||
userEntityEngineDescription,
|
||||
serviceEntityEngineDescription,
|
||||
genericEntityEngineDescription,
|
||||
} from '../entity_definitions/entity_descriptions';
|
||||
import type { EntityStoreConfig } from '../types';
|
||||
import { buildEntityDefinitionId, mergeEntityStoreIndices } from '../utils';
|
||||
|
@ -27,6 +28,7 @@ const engineDescriptionRegistry: Record<EntityType, EntityDescription> = {
|
|||
host: hostEntityEngineDescription,
|
||||
user: userEntityEngineDescription,
|
||||
service: serviceEntityEngineDescription,
|
||||
generic: genericEntityEngineDescription,
|
||||
};
|
||||
|
||||
interface EngineDescriptionParams {
|
||||
|
|
|
@ -108,6 +108,7 @@ export const registerEntityStoreDataViewRefreshTask = ({
|
|||
config: entityStoreConfig,
|
||||
security,
|
||||
request,
|
||||
uiSettingsClient: core.uiSettings.asScopedToClient(soClient),
|
||||
});
|
||||
|
||||
const { errors } = await entityStoreClient.applyDataViewIndices();
|
||||
|
|
|
@ -14,7 +14,7 @@ import type {
|
|||
TaskManagerStartContract,
|
||||
} from '@kbn/task-manager-plugin/server';
|
||||
import type { ExperimentalFeatures } from '../../../../../../common';
|
||||
import { getEnabledStoreEntityTypes } from '../../../../../../common/entity_analytics/entity_store/utils';
|
||||
import { getEntityAnalyticsEntityTypes } from '../../../../../../common/entity_analytics/utils';
|
||||
import {
|
||||
EngineComponentResourceEnum,
|
||||
type EntityType,
|
||||
|
@ -201,7 +201,7 @@ export const runEntityStoreFieldRetentionEnrichTask = async ({
|
|||
return { state: updatedState };
|
||||
}
|
||||
|
||||
const entityTypes = getEnabledStoreEntityTypes(experimentalFeatures);
|
||||
const entityTypes = getEntityAnalyticsEntityTypes();
|
||||
|
||||
for (const entityType of entityTypes) {
|
||||
const start = Date.now();
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
ALERT_WORKFLOW_STATUS,
|
||||
ALERT_WORKFLOW_TAGS,
|
||||
} from '@kbn/rule-registry-plugin/common/technical_rule_data_field_names';
|
||||
import { getRiskEngineEntityTypes } from '../../../../common/entity_analytics/risk_engine/utils';
|
||||
import { getEntityAnalyticsEntityTypes } from '../../../../common/entity_analytics/utils';
|
||||
import type { EntityType } from '../../../../common/search_strategy';
|
||||
import type { ExperimentalFeatures } from '../../../../common';
|
||||
import type {
|
||||
|
@ -249,7 +249,7 @@ export const calculateRiskScores = async ({
|
|||
}
|
||||
const identifierTypes: EntityType[] = identifierType
|
||||
? [identifierType]
|
||||
: getRiskEngineEntityTypes(experimentalFeatures);
|
||||
: getEntityAnalyticsEntityTypes();
|
||||
|
||||
const request = {
|
||||
size: 0,
|
||||
|
|
|
@ -16,7 +16,7 @@ import type {
|
|||
} from '@kbn/task-manager-plugin/server';
|
||||
import type { AnalyticsServiceSetup } from '@kbn/core-analytics-server';
|
||||
import type { AuditLogger } from '@kbn/security-plugin-types-server';
|
||||
import { getRiskEngineEntityTypes } from '../../../../../common/entity_analytics/risk_engine/utils';
|
||||
import { getEntityAnalyticsEntityTypes } from '../../../../../common/entity_analytics/utils';
|
||||
import type { EntityType } from '../../../../../common/search_strategy';
|
||||
import type { ExperimentalFeatures } from '../../../../../common';
|
||||
import type { AfterKeys } from '../../../../../common/api/entity_analytics/common';
|
||||
|
@ -291,7 +291,7 @@ export const runTask = async ({
|
|||
|
||||
const identifierTypes: EntityType[] = configuredIdentifierType
|
||||
? [configuredIdentifierType]
|
||||
: getRiskEngineEntityTypes(experimentalFeatures);
|
||||
: getEntityAnalyticsEntityTypes();
|
||||
|
||||
const runs: Array<{
|
||||
identifierType: EntityType;
|
||||
|
|
|
@ -289,6 +289,7 @@ export class RequestContextFactory implements IRequestContextFactory {
|
|||
apiKeyManager: getEntityStoreApiKeyManager(),
|
||||
security: startPlugins.security,
|
||||
request,
|
||||
uiSettingsClient: coreContext.uiSettings.client,
|
||||
});
|
||||
}),
|
||||
getAssetInventoryClient: memoize(
|
||||
|
|
|
@ -45,6 +45,11 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
await utils.initEntityEngineForEntityTypesAndWait(['host']);
|
||||
await utils.expectEngineAssetsExist('host');
|
||||
});
|
||||
|
||||
it('should have installed the expected generic resources', async () => {
|
||||
await utils.initEntityEngineForEntityTypesAndWait(['generic']);
|
||||
await utils.expectEngineAssetsExist('generic');
|
||||
});
|
||||
});
|
||||
|
||||
describe('init error handling', () => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue