[Security Solution][Entity Analytics] APIs for Entity Store engine (#191986)

This PR introduces the following API routes for setting up Entity Store
"engines":

<meta charset="utf-8"><b style="font-weight:normal;"
id="docs-internal-guid-9410c5d7-7fff-e873-6830-887939a306fb"><div
dir="ltr" style="margin-left:-0.75pt;" align="left">
Initialise Engine | POST /api/entity_store/engines/<entity_type>/init
-- | --
Start Engine | POST /api/entity_store/engines/<entity_type>/start
Stop Engine | POST /api/entity_store/engines/<entity_type>/stop
Delete Engine | DELETE /api/entity_store/engines/<entity_type>
Get engine | GET  /api/entity_store/engines/<entity_type>
List Engines | GET /api/entity_store/engines

</div></b>

The PR includes the following:
- Adding the `EntityManager` plugin (see elastic/obs-entities) as a
dependency of the Security Solution
 - The OpenAPI schemas for the new routes
 - The actual Kibana side endpoints
 - A `Saved Object` to track the installed engines
 - A new `EntityStoreDataClient`
 - A new feature flag `entityStoreEngineRoutesEnabled` 
 

### How to test

1. Add some host/user data
* Easiest is to use
[elastic/security-data-generator](https://github.com/elastic/security-documents-generator)
2. Make sure to add `entityStoreEngineRoutesEnabled` under
`xpack.securitySolution.enableExperimental` in your `kibana.dev.yml`
3. In kibana dev tools or your terminal, call the `INIT` route for
either `user` or `host`.
4. You should now see 2 transforms in kibana. Make sure to re-trigger
them if needed so they process the documents.
5. Check that new entities have been observed by querying the new
entities index via:
    *  `GET .entities.v1.latest.ea*/_search`
6. Check the other endpoints are working (`START`, `STOP`, `LIST`, etc)
7. Calling `DELETE` should remove the transforms



Implements https://github.com/elastic/security-team/issues/10230

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Tiago Vila Verde 2024-09-16 17:15:10 +02:00 committed by GitHub
parent f029f8086a
commit cd964f1229
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
50 changed files with 2097 additions and 2 deletions

View file

@ -312,6 +312,12 @@
"entity-discovery-api-key": [
"apiKey"
],
"entity-engine-status": [
"filter",
"indexPattern",
"status",
"type"
],
"epm-packages": [
"additional_spaces_installed_kibana",
"es_index_patterns",

View file

@ -1057,6 +1057,23 @@
}
}
},
"entity-engine-status": {
"dynamic": false,
"properties": {
"filter": {
"type": "keyword"
},
"indexPattern": {
"type": "keyword"
},
"status": {
"type": "keyword"
},
"type": {
"type": "keyword"
}
}
},
"epm-packages": {
"properties": {
"additional_spaces_installed_kibana": {

View file

@ -93,6 +93,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
"enterprise_search_telemetry": "9ac912e1417fc8681e0cd383775382117c9e3d3d",
"entity-definition": "61be3e95966045122b55e181bb39658b1dc9bbe9",
"entity-discovery-api-key": "c267a65c69171d1804362155c1378365f5acef88",
"entity-engine-status": "0738aa1a06d3361911740f8f166071ea43a00927",
"epm-packages": "8042d4a1522f6c4e6f5486e791b3ffe3a22f88fd",
"epm-packages-assets": "7a3e58efd9a14191d0d1a00b8aaed30a145fd0b1",
"event-annotation-group": "715ba867d8c68f3c9438052210ea1c30a9362582",

View file

@ -124,6 +124,7 @@ const previouslyRegisteredTypes = [
'security-rule',
'security-solution-signals-migration',
'risk-engine-configuration',
'entity-engine-status',
'server',
'siem-detection-engine-rule-actions',
'siem-detection-engine-rule-execution-info',

View file

@ -0,0 +1,38 @@
/*
* 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.
*/
/*
* NOTICE: Do not edit this file manually.
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
*
* info:
* title: Entity Store Common Schema
* version: 1
*/
import { z } from '@kbn/zod';
export type EntityType = z.infer<typeof EntityType>;
export const EntityType = z.enum(['user', 'host']);
export type EntityTypeEnum = typeof EntityType.enum;
export const EntityTypeEnum = EntityType.enum;
export type IndexPattern = z.infer<typeof IndexPattern>;
export const IndexPattern = z.string();
export type EngineStatus = z.infer<typeof EngineStatus>;
export const EngineStatus = z.enum(['installing', 'started', 'stopped']);
export type EngineStatusEnum = typeof EngineStatus.enum;
export const EngineStatusEnum = EngineStatus.enum;
export type EngineDescriptor = z.infer<typeof EngineDescriptor>;
export const EngineDescriptor = z.object({
type: EntityType.optional(),
indexPattern: IndexPattern.optional(),
status: EngineStatus.optional(),
filter: z.string().optional(),
});

View file

@ -0,0 +1,37 @@
openapi: 3.0.0
info:
title: Entity Store Common Schema
description: Common schema for Entity Store
version: '1'
paths: {}
components:
schemas:
EntityType:
type: string
enum:
- user
- host
EngineDescriptor:
type: object
properties:
type:
$ref: '#/components/schemas/EntityType'
indexPattern:
$ref: '#/components/schemas/IndexPattern'
status:
$ref: '#/components/schemas/EngineStatus'
filter:
type: string
EngineStatus:
type: string
enum:
- installing
- started
- stopped
IndexPattern:
type: string

View file

@ -0,0 +1,43 @@
/*
* 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.
*/
/*
* NOTICE: Do not edit this file manually.
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
*
* info:
* title: Delete the entity store engine
* version: 2023-10-31
*/
import { z } from '@kbn/zod';
import { BooleanFromString } from '@kbn/zod-helpers';
import { EntityType } from '../common.gen';
export type DeleteEntityStoreRequestQuery = z.infer<typeof DeleteEntityStoreRequestQuery>;
export const DeleteEntityStoreRequestQuery = z.object({
/**
* Control flag to also delete the entity data.
*/
data: BooleanFromString.optional(),
});
export type DeleteEntityStoreRequestQueryInput = z.input<typeof DeleteEntityStoreRequestQuery>;
export type DeleteEntityStoreRequestParams = z.infer<typeof DeleteEntityStoreRequestParams>;
export const DeleteEntityStoreRequestParams = z.object({
/**
* The entity type of the store (either 'user' or 'host').
*/
entityType: EntityType,
});
export type DeleteEntityStoreRequestParamsInput = z.input<typeof DeleteEntityStoreRequestParams>;
export type DeleteEntityStoreResponse = z.infer<typeof DeleteEntityStoreResponse>;
export const DeleteEntityStoreResponse = z.object({
deleted: z.boolean().optional(),
});

View file

@ -0,0 +1,37 @@
openapi: 3.0.0
info:
title: Delete the entity store engine
version: '2023-10-31'
paths:
/api/entity_store/engines/{entityType}:
delete:
x-labels: [ess, serverless]
x-codegen-enabled: true
operationId: DeleteEntityStore
summary: Delete the Entity Store engine
parameters:
- name: entityType
in: path
required: true
schema:
$ref: '../common.schema.yaml#/components/schemas/EntityType'
description: The entity type of the store (either 'user' or 'host').
- name: data
in: query
required: false
schema:
type: boolean
description: Control flag to also delete the entity data.
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: object
properties:
deleted:
type: boolean

View file

@ -0,0 +1,33 @@
/*
* 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.
*/
/*
* NOTICE: Do not edit this file manually.
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
*
* info:
* title: Get Entity Store engine
* version: 2023-10-31
*/
import { z } from '@kbn/zod';
import { EntityType, EngineDescriptor } from '../common.gen';
export type GetEntityStoreEngineRequestParams = z.infer<typeof GetEntityStoreEngineRequestParams>;
export const GetEntityStoreEngineRequestParams = z.object({
/**
* The entity type of the store (either 'user' or 'host').
*/
entityType: EntityType,
});
export type GetEntityStoreEngineRequestParamsInput = z.input<
typeof GetEntityStoreEngineRequestParams
>;
export type GetEntityStoreEngineResponse = z.infer<typeof GetEntityStoreEngineResponse>;
export const GetEntityStoreEngineResponse = EngineDescriptor;

View file

@ -0,0 +1,25 @@
openapi: 3.0.0
info:
title: Get Entity Store engine
version: '2023-10-31'
paths:
/api/entity_store/engines/{entityType}:
get:
x-labels: [ess, serverless]
x-codegen-enabled: true
operationId: GetEntityStoreEngine
summary: Get the Entity Store engine
parameters:
- name: entityType
in: path
required: true
schema:
$ref: '../common.schema.yaml#/components/schemas/EntityType'
description: The entity type of the store (either 'user' or 'host').
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '../common.schema.yaml#/components/schemas/EngineDescriptor'

View file

@ -0,0 +1,38 @@
/*
* 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.
*/
/*
* NOTICE: Do not edit this file manually.
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
*
* info:
* title: Init Entity Store types
* version: 2023-10-31
*/
import { z } from '@kbn/zod';
import { EntityType, IndexPattern, EngineDescriptor } from '../common.gen';
export type InitEntityStoreRequestParams = z.infer<typeof InitEntityStoreRequestParams>;
export const InitEntityStoreRequestParams = z.object({
/**
* The entity type of the store (either 'user' or 'host').
*/
entityType: EntityType,
});
export type InitEntityStoreRequestParamsInput = z.input<typeof InitEntityStoreRequestParams>;
export type InitEntityStoreRequestBody = z.infer<typeof InitEntityStoreRequestBody>;
export const InitEntityStoreRequestBody = z.object({
indexPattern: IndexPattern.optional(),
filter: z.string().optional(),
});
export type InitEntityStoreRequestBodyInput = z.input<typeof InitEntityStoreRequestBody>;
export type InitEntityStoreResponse = z.infer<typeof InitEntityStoreResponse>;
export const InitEntityStoreResponse = EngineDescriptor;

View file

@ -0,0 +1,39 @@
openapi: 3.0.0
info:
title: Init Entity Store types
version: '2023-10-31'
paths:
/api/entity_store/engines/{entityType}/init:
post:
x-labels: [ess, serverless]
x-codegen-enabled: true
operationId: InitEntityStore
summary: Initialize the Entity Store
parameters:
- name: entityType
in: path
required: true
schema:
$ref: '../common.schema.yaml#/components/schemas/EntityType'
description: The entity type of the store (either 'user' or 'host').
requestBody:
description: Schema for the engine initialization
required: true
content:
application/json:
schema:
type: object
properties:
indexPattern:
$ref: '../common.schema.yaml#/components/schemas/IndexPattern'
filter:
type: string
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '../common.schema.yaml#/components/schemas/EngineDescriptor'

View file

@ -0,0 +1,25 @@
/*
* 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.
*/
/*
* NOTICE: Do not edit this file manually.
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
*
* info:
* title: List Entity Store engines
* version: 2023-10-31
*/
import { z } from '@kbn/zod';
import { EngineDescriptor } from '../common.gen';
export type ListEntityStoreEnginesResponse = z.infer<typeof ListEntityStoreEnginesResponse>;
export const ListEntityStoreEnginesResponse = z.object({
count: z.number().int().optional(),
engines: z.array(EngineDescriptor).optional(),
});

View file

@ -0,0 +1,25 @@
openapi: 3.0.0
info:
title: List Entity Store engines
version: '2023-10-31'
paths:
/api/entity_store/engines:
get:
x-labels: [ess, serverless]
x-codegen-enabled: true
operationId: ListEntityStoreEngines
summary: List the Entity Store engines
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: object
properties:
count:
type: integer
engines:
type: array
items:
$ref: '../common.schema.yaml#/components/schemas/EngineDescriptor'

View file

@ -0,0 +1,33 @@
/*
* 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.
*/
/*
* NOTICE: Do not edit this file manually.
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
*
* info:
* title: Start the entity store engine
* version: 2023-10-31
*/
import { z } from '@kbn/zod';
import { EntityType } from '../common.gen';
export type StartEntityStoreRequestParams = z.infer<typeof StartEntityStoreRequestParams>;
export const StartEntityStoreRequestParams = z.object({
/**
* The entity type of the store (either 'user' or 'host').
*/
entityType: EntityType,
});
export type StartEntityStoreRequestParamsInput = z.input<typeof StartEntityStoreRequestParams>;
export type StartEntityStoreResponse = z.infer<typeof StartEntityStoreResponse>;
export const StartEntityStoreResponse = z.object({
started: z.boolean().optional(),
});

View file

@ -0,0 +1,31 @@
openapi: 3.0.0
info:
title: Start the entity store engine
version: '2023-10-31'
paths:
/api/entity_store/engines/{entityType}/start:
post:
x-labels: [ess, serverless]
x-codegen-enabled: true
operationId: StartEntityStore
summary: Start the Entity Store engine
parameters:
- name: entityType
in: path
required: true
schema:
$ref: '../common.schema.yaml#/components/schemas/EntityType'
description: The entity type of the store (either 'user' or 'host').
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: object
properties:
started:
type: boolean

View file

@ -0,0 +1,39 @@
/*
* 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.
*/
/*
* NOTICE: Do not edit this file manually.
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
*
* info:
* title: Get the entity store engine stats
* version: 2023-10-31
*/
import { z } from '@kbn/zod';
import { EntityType, IndexPattern, EngineStatus } from '../common.gen';
export type GetEntityStoreStatsRequestParams = z.infer<typeof GetEntityStoreStatsRequestParams>;
export const GetEntityStoreStatsRequestParams = z.object({
/**
* The entity type of the store (either 'user' or 'host').
*/
entityType: EntityType,
});
export type GetEntityStoreStatsRequestParamsInput = z.input<
typeof GetEntityStoreStatsRequestParams
>;
export type GetEntityStoreStatsResponse = z.infer<typeof GetEntityStoreStatsResponse>;
export const GetEntityStoreStatsResponse = z.object({
type: EntityType.optional(),
indexPattern: IndexPattern.optional(),
status: EngineStatus.optional(),
transforms: z.array(z.object({})).optional(),
indices: z.array(z.object({})).optional(),
});

View file

@ -0,0 +1,41 @@
openapi: 3.0.0
info:
title: Get the entity store engine stats
version: '2023-10-31'
paths:
/api/entity_store/engines/{entityType}/stats:
post:
x-labels: [ess, serverless]
x-codegen-enabled: true
operationId: GetEntityStoreStats
summary: Get the Entity Store engine stats
parameters:
- name: entityType
in: path
required: true
schema:
$ref: '../common.schema.yaml#/components/schemas/EntityType'
description: The entity type of the store (either 'user' or 'host').
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: object
properties:
type:
$ref : '../common.schema.yaml#/components/schemas/EntityType'
indexPattern:
$ref : '../common.schema.yaml#/components/schemas/IndexPattern'
status:
$ref : '../common.schema.yaml#/components/schemas/EngineStatus'
transforms:
type: array
items:
type: object
indices:
type: array
items:
type: object

View file

@ -0,0 +1,33 @@
/*
* 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.
*/
/*
* NOTICE: Do not edit this file manually.
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
*
* info:
* title: Stop the entity store engine
* version: 2023-10-31
*/
import { z } from '@kbn/zod';
import { EntityType } from '../common.gen';
export type StopEntityStoreRequestParams = z.infer<typeof StopEntityStoreRequestParams>;
export const StopEntityStoreRequestParams = z.object({
/**
* The entity type of the store (either 'user' or 'host').
*/
entityType: EntityType,
});
export type StopEntityStoreRequestParamsInput = z.input<typeof StopEntityStoreRequestParams>;
export type StopEntityStoreResponse = z.infer<typeof StopEntityStoreResponse>;
export const StopEntityStoreResponse = z.object({
stopped: z.boolean().optional(),
});

View file

@ -0,0 +1,30 @@
openapi: 3.0.0
info:
title: Stop the entity store engine
version: '2023-10-31'
paths:
/api/entity_store/engines/{entityType}/stop:
post:
x-labels: [ess, serverless]
x-codegen-enabled: true
operationId: StopEntityStore
summary: Stop the Entity Store engine
parameters:
- name: entityType
in: path
required: true
schema:
$ref: '../common.schema.yaml#/components/schemas/EntityType'
description: The entity type of the store (either 'user' or 'host').
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: object
properties:
stopped:
type: boolean

View file

@ -242,6 +242,33 @@ import type {
InternalUploadAssetCriticalityRecordsResponse,
UploadAssetCriticalityRecordsResponse,
} from './entity_analytics/asset_criticality/upload_asset_criticality_csv.gen';
import type {
DeleteEntityStoreRequestQueryInput,
DeleteEntityStoreRequestParamsInput,
DeleteEntityStoreResponse,
} from './entity_analytics/entity_store/engine/delete.gen';
import type {
GetEntityStoreEngineRequestParamsInput,
GetEntityStoreEngineResponse,
} from './entity_analytics/entity_store/engine/get.gen';
import type {
InitEntityStoreRequestParamsInput,
InitEntityStoreRequestBodyInput,
InitEntityStoreResponse,
} from './entity_analytics/entity_store/engine/init.gen';
import type { ListEntityStoreEnginesResponse } from './entity_analytics/entity_store/engine/list.gen';
import type {
StartEntityStoreRequestParamsInput,
StartEntityStoreResponse,
} from './entity_analytics/entity_store/engine/start.gen';
import type {
GetEntityStoreStatsRequestParamsInput,
GetEntityStoreStatsResponse,
} from './entity_analytics/entity_store/engine/stats.gen';
import type {
StopEntityStoreRequestParamsInput,
StopEntityStoreResponse,
} from './entity_analytics/entity_store/engine/stop.gen';
import type { DisableRiskEngineResponse } from './entity_analytics/risk_engine/engine_disable_route.gen';
import type { EnableRiskEngineResponse } from './entity_analytics/risk_engine/engine_enable_route.gen';
import type { InitRiskEngineResponse } from './entity_analytics/risk_engine/engine_init_route.gen';
@ -620,6 +647,20 @@ Migrations are initiated per index. While the process is neither destructive nor
})
.catch(catchAxiosErrorFormatAndThrow);
}
async deleteEntityStore(props: DeleteEntityStoreProps) {
this.log.info(`${new Date().toISOString()} Calling API DeleteEntityStore`);
return this.kbnClient
.request<DeleteEntityStoreResponse>({
path: replaceParams('/api/entity_store/engines/{entityType}', props.params),
headers: {
[ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31',
},
method: 'DELETE',
query: props.query,
})
.catch(catchAxiosErrorFormatAndThrow);
}
async deleteNote(props: DeleteNoteProps) {
this.log.info(`${new Date().toISOString()} Calling API DeleteNote`);
return this.kbnClient
@ -1155,6 +1196,30 @@ finalize it.
})
.catch(catchAxiosErrorFormatAndThrow);
}
async getEntityStoreEngine(props: GetEntityStoreEngineProps) {
this.log.info(`${new Date().toISOString()} Calling API GetEntityStoreEngine`);
return this.kbnClient
.request<GetEntityStoreEngineResponse>({
path: replaceParams('/api/entity_store/engines/{entityType}', props.params),
headers: {
[ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31',
},
method: 'GET',
})
.catch(catchAxiosErrorFormatAndThrow);
}
async getEntityStoreStats(props: GetEntityStoreStatsProps) {
this.log.info(`${new Date().toISOString()} Calling API GetEntityStoreStats`);
return this.kbnClient
.request<GetEntityStoreStatsResponse>({
path: replaceParams('/api/entity_store/engines/{entityType}/stats', props.params),
headers: {
[ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31',
},
method: 'POST',
})
.catch(catchAxiosErrorFormatAndThrow);
}
/**
* Gets notes
*/
@ -1311,6 +1376,19 @@ finalize it.
})
.catch(catchAxiosErrorFormatAndThrow);
}
async initEntityStore(props: InitEntityStoreProps) {
this.log.info(`${new Date().toISOString()} Calling API InitEntityStore`);
return this.kbnClient
.request<InitEntityStoreResponse>({
path: replaceParams('/api/entity_store/engines/{entityType}/init', props.params),
headers: {
[ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31',
},
method: 'POST',
body: props.body,
})
.catch(catchAxiosErrorFormatAndThrow);
}
/**
* Initializes the Risk Engine by creating the necessary indices and mappings, removing old transforms, and starting the new risk engine
*/
@ -1367,6 +1445,18 @@ finalize it.
})
.catch(catchAxiosErrorFormatAndThrow);
}
async listEntityStoreEngines() {
this.log.info(`${new Date().toISOString()} Calling API ListEntityStoreEngines`);
return this.kbnClient
.request<ListEntityStoreEnginesResponse>({
path: '/api/entity_store/engines',
headers: {
[ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31',
},
method: 'GET',
})
.catch(catchAxiosErrorFormatAndThrow);
}
/**
* Update specific fields of an existing detection rule using the `rule_id` or `id` field.
*/
@ -1699,6 +1789,30 @@ detection engine rules.
})
.catch(catchAxiosErrorFormatAndThrow);
}
async startEntityStore(props: StartEntityStoreProps) {
this.log.info(`${new Date().toISOString()} Calling API StartEntityStore`);
return this.kbnClient
.request<StartEntityStoreResponse>({
path: replaceParams('/api/entity_store/engines/{entityType}/start', props.params),
headers: {
[ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31',
},
method: 'POST',
})
.catch(catchAxiosErrorFormatAndThrow);
}
async stopEntityStore(props: StopEntityStoreProps) {
this.log.info(`${new Date().toISOString()} Calling API StopEntityStore`);
return this.kbnClient
.request<StopEntityStoreResponse>({
path: replaceParams('/api/entity_store/engines/{entityType}/stop', props.params),
headers: {
[ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31',
},
method: 'POST',
})
.catch(catchAxiosErrorFormatAndThrow);
}
/**
* Suggests user profiles.
*/
@ -1809,6 +1923,10 @@ export interface CreateUpdateProtectionUpdatesNoteProps {
export interface DeleteAssetCriticalityRecordProps {
query: DeleteAssetCriticalityRecordRequestQueryInput;
}
export interface DeleteEntityStoreProps {
query: DeleteEntityStoreRequestQueryInput;
params: DeleteEntityStoreRequestParamsInput;
}
export interface DeleteNoteProps {
body: DeleteNoteRequestBodyInput;
}
@ -1902,6 +2020,12 @@ export interface GetEndpointSuggestionsProps {
params: GetEndpointSuggestionsRequestParamsInput;
body: GetEndpointSuggestionsRequestBodyInput;
}
export interface GetEntityStoreEngineProps {
params: GetEntityStoreEngineRequestParamsInput;
}
export interface GetEntityStoreStatsProps {
params: GetEntityStoreStatsRequestParamsInput;
}
export interface GetNotesProps {
query: GetNotesRequestQueryInput;
}
@ -1932,6 +2056,10 @@ export interface ImportRulesProps {
export interface ImportTimelinesProps {
body: ImportTimelinesRequestBodyInput;
}
export interface InitEntityStoreProps {
params: InitEntityStoreRequestParamsInput;
body: InitEntityStoreRequestBodyInput;
}
export interface InstallPrepackedTimelinesProps {
body: InstallPrepackedTimelinesRequestBodyInput;
}
@ -1984,6 +2112,12 @@ export interface SetAlertsStatusProps {
export interface SetAlertTagsProps {
body: SetAlertTagsRequestBodyInput;
}
export interface StartEntityStoreProps {
params: StartEntityStoreRequestParamsInput;
}
export interface StopEntityStoreProps {
params: StopEntityStoreRequestParamsInput;
}
export interface SuggestUserProfilesProps {
query: SuggestUserProfilesRequestQueryInput;
}

View file

@ -223,6 +223,11 @@ export const allowedExperimentalValues = Object.freeze({
* Enables the new data ingestion hub
*/
dataIngestionHubEnabled: false,
/**
* Enables the new Entity Store engine routes
*/
entityStoreEnabled: false,
});
type ExperimentalConfigKeys = Array<keyof ExperimentalFeatures>;

View file

@ -256,6 +256,187 @@ paths:
summary: List Asset Criticality Records
tags:
- Security Solution Entity Analytics API
/api/entity_store/engines:
get:
operationId: ListEntityStoreEngines
responses:
'200':
content:
application/json:
schema:
type: object
properties:
count:
type: integer
engines:
items:
$ref: '#/components/schemas/EngineDescriptor'
type: array
description: Successful response
summary: List the Entity Store engines
tags:
- Security Solution Entity Analytics API
'/api/entity_store/engines/{entityType}':
delete:
operationId: DeleteEntityStore
parameters:
- description: The entity type of the store (either 'user' or 'host').
in: path
name: entityType
required: true
schema:
$ref: '#/components/schemas/EntityType'
- description: Control flag to also delete the entity data.
in: query
name: data
required: false
schema:
type: boolean
responses:
'200':
content:
application/json:
schema:
type: object
properties:
deleted:
type: boolean
description: Successful response
summary: Delete the Entity Store engine
tags:
- Security Solution Entity Analytics API
get:
operationId: GetEntityStoreEngine
parameters:
- description: The entity type of the store (either 'user' or 'host').
in: path
name: entityType
required: true
schema:
$ref: '#/components/schemas/EntityType'
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/EngineDescriptor'
description: Successful response
summary: Get the Entity Store engine
tags:
- Security Solution Entity Analytics API
'/api/entity_store/engines/{entityType}/init':
post:
operationId: InitEntityStore
parameters:
- description: The entity type of the store (either 'user' or 'host').
in: path
name: entityType
required: true
schema:
$ref: '#/components/schemas/EntityType'
requestBody:
content:
application/json:
schema:
type: object
properties:
filter:
type: string
indexPattern:
$ref: '#/components/schemas/IndexPattern'
description: Schema for the engine initialization
required: true
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/EngineDescriptor'
description: Successful response
summary: Initialize the Entity Store
tags:
- Security Solution Entity Analytics API
'/api/entity_store/engines/{entityType}/start':
post:
operationId: StartEntityStore
parameters:
- description: The entity type of the store (either 'user' or 'host').
in: path
name: entityType
required: true
schema:
$ref: '#/components/schemas/EntityType'
responses:
'200':
content:
application/json:
schema:
type: object
properties:
started:
type: boolean
description: Successful response
summary: Start the Entity Store engine
tags:
- Security Solution Entity Analytics API
'/api/entity_store/engines/{entityType}/stats':
post:
operationId: GetEntityStoreStats
parameters:
- description: The entity type of the store (either 'user' or 'host').
in: path
name: entityType
required: true
schema:
$ref: '#/components/schemas/EntityType'
responses:
'200':
content:
application/json:
schema:
type: object
properties:
indexPattern:
$ref: '#/components/schemas/IndexPattern'
indices:
items:
type: object
type: array
status:
$ref: '#/components/schemas/EngineStatus'
transforms:
items:
type: object
type: array
type:
$ref: '#/components/schemas/EntityType'
description: Successful response
summary: Get the Entity Store engine stats
tags:
- Security Solution Entity Analytics API
'/api/entity_store/engines/{entityType}/stop':
post:
operationId: StopEntityStore
parameters:
- description: The entity type of the store (either 'user' or 'host').
in: path
name: entityType
required: true
schema:
$ref: '#/components/schemas/EntityType'
responses:
'200':
content:
application/json:
schema:
type: object
properties:
stopped:
type: boolean
description: Successful response
summary: Stop the Entity Store engine
tags:
- Security Solution Entity Analytics API
/api/risk_score/engine/schedule_now:
post:
operationId: ScheduleRiskEngineNow
@ -351,11 +532,35 @@ components:
$ref: '#/components/schemas/AssetCriticalityLevel'
required:
- criticality_level
EngineDescriptor:
type: object
properties:
filter:
type: string
indexPattern:
$ref: '#/components/schemas/IndexPattern'
status:
$ref: '#/components/schemas/EngineStatus'
type:
$ref: '#/components/schemas/EntityType'
EngineStatus:
enum:
- installing
- started
- stopped
type: string
EntityType:
enum:
- user
- host
type: string
IdField:
enum:
- host.name
- user.name
type: string
IndexPattern:
type: string
RiskEngineScheduleNowErrorResponse:
type: object
properties:

View file

@ -256,6 +256,187 @@ paths:
summary: List Asset Criticality Records
tags:
- Security Solution Entity Analytics API
/api/entity_store/engines:
get:
operationId: ListEntityStoreEngines
responses:
'200':
content:
application/json:
schema:
type: object
properties:
count:
type: integer
engines:
items:
$ref: '#/components/schemas/EngineDescriptor'
type: array
description: Successful response
summary: List the Entity Store engines
tags:
- Security Solution Entity Analytics API
'/api/entity_store/engines/{entityType}':
delete:
operationId: DeleteEntityStore
parameters:
- description: The entity type of the store (either 'user' or 'host').
in: path
name: entityType
required: true
schema:
$ref: '#/components/schemas/EntityType'
- description: Control flag to also delete the entity data.
in: query
name: data
required: false
schema:
type: boolean
responses:
'200':
content:
application/json:
schema:
type: object
properties:
deleted:
type: boolean
description: Successful response
summary: Delete the Entity Store engine
tags:
- Security Solution Entity Analytics API
get:
operationId: GetEntityStoreEngine
parameters:
- description: The entity type of the store (either 'user' or 'host').
in: path
name: entityType
required: true
schema:
$ref: '#/components/schemas/EntityType'
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/EngineDescriptor'
description: Successful response
summary: Get the Entity Store engine
tags:
- Security Solution Entity Analytics API
'/api/entity_store/engines/{entityType}/init':
post:
operationId: InitEntityStore
parameters:
- description: The entity type of the store (either 'user' or 'host').
in: path
name: entityType
required: true
schema:
$ref: '#/components/schemas/EntityType'
requestBody:
content:
application/json:
schema:
type: object
properties:
filter:
type: string
indexPattern:
$ref: '#/components/schemas/IndexPattern'
description: Schema for the engine initialization
required: true
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/EngineDescriptor'
description: Successful response
summary: Initialize the Entity Store
tags:
- Security Solution Entity Analytics API
'/api/entity_store/engines/{entityType}/start':
post:
operationId: StartEntityStore
parameters:
- description: The entity type of the store (either 'user' or 'host').
in: path
name: entityType
required: true
schema:
$ref: '#/components/schemas/EntityType'
responses:
'200':
content:
application/json:
schema:
type: object
properties:
started:
type: boolean
description: Successful response
summary: Start the Entity Store engine
tags:
- Security Solution Entity Analytics API
'/api/entity_store/engines/{entityType}/stats':
post:
operationId: GetEntityStoreStats
parameters:
- description: The entity type of the store (either 'user' or 'host').
in: path
name: entityType
required: true
schema:
$ref: '#/components/schemas/EntityType'
responses:
'200':
content:
application/json:
schema:
type: object
properties:
indexPattern:
$ref: '#/components/schemas/IndexPattern'
indices:
items:
type: object
type: array
status:
$ref: '#/components/schemas/EngineStatus'
transforms:
items:
type: object
type: array
type:
$ref: '#/components/schemas/EntityType'
description: Successful response
summary: Get the Entity Store engine stats
tags:
- Security Solution Entity Analytics API
'/api/entity_store/engines/{entityType}/stop':
post:
operationId: StopEntityStore
parameters:
- description: The entity type of the store (either 'user' or 'host').
in: path
name: entityType
required: true
schema:
$ref: '#/components/schemas/EntityType'
responses:
'200':
content:
application/json:
schema:
type: object
properties:
stopped:
type: boolean
description: Successful response
summary: Stop the Entity Store engine
tags:
- Security Solution Entity Analytics API
/api/risk_score/engine/schedule_now:
post:
operationId: ScheduleRiskEngineNow
@ -351,11 +532,35 @@ components:
$ref: '#/components/schemas/AssetCriticalityLevel'
required:
- criticality_level
EngineDescriptor:
type: object
properties:
filter:
type: string
indexPattern:
$ref: '#/components/schemas/IndexPattern'
status:
$ref: '#/components/schemas/EngineStatus'
type:
$ref: '#/components/schemas/EntityType'
EngineStatus:
enum:
- installing
- started
- stopped
type: string
EntityType:
enum:
- user
- host
type: string
IdField:
enum:
- host.name
- user.name
type: string
IndexPattern:
type: string
RiskEngineScheduleNowErrorResponse:
type: object
properties:

View file

@ -53,7 +53,8 @@
"notifications",
"savedSearch",
"unifiedDocViewer",
"charts"
"charts",
"entityManager"
],
"optionalPlugins": [
"cloudExperiments",
@ -87,4 +88,4 @@
"common"
]
}
}
}

View file

@ -36,6 +36,7 @@ import { getEndpointAuthzInitialStateMock } from '../../../../../common/endpoint
import type { EndpointAuthz } from '../../../../../common/endpoint/types/authz';
import { riskEngineDataClientMock } from '../../../entity_analytics/risk_engine/risk_engine_data_client.mock';
import { riskScoreDataClientMock } from '../../../entity_analytics/risk_score/risk_score_data_client.mock';
import { entityStoreDataClientMock } from '../../../entity_analytics/entity_store/entity_store_data_client.mock';
import { assetCriticalityDataClientMock } from '../../../entity_analytics/asset_criticality/asset_criticality_data_client.mock';
import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks';
import { detectionRulesClientMock } from '../../rule_management/logic/detection_rules_client/__mocks__/detection_rules_client';
@ -72,6 +73,7 @@ export const createMockClients = () => {
riskEngineDataClient: riskEngineDataClientMock.create(),
riskScoreDataClient: riskScoreDataClientMock.create(),
assetCriticalityDataClient: assetCriticalityDataClientMock.create(),
entityStoreDataClient: entityStoreDataClientMock.create(),
internalFleetServices: {
packages: packageServiceMock.createClient(),
@ -159,6 +161,7 @@ const createSecuritySolutionRequestContextMock = (
getRiskScoreDataClient: jest.fn(() => clients.riskScoreDataClient),
getAssetCriticalityDataClient: jest.fn(() => clients.assetCriticalityDataClient),
getAuditLogger: jest.fn(() => mockAuditLogger),
getEntityStoreDataClient: jest.fn(() => clients.entityStoreDataClient),
};
};

View file

@ -0,0 +1,21 @@
/*
* 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 { EngineStatus } from '../../../../common/api/entity_analytics/entity_store/common.gen';
import { DEFAULT_INDEX_PATTERN } from '../../../../common/constants';
/**
* Default index pattern for entity store
* This is the same as the default index pattern for the SIEM app but might diverge in the future
*/
export const ENTITY_STORE_DEFAULT_SOURCE_INDICES = DEFAULT_INDEX_PATTERN;
export const ENGINE_STATUS: Record<Uppercase<EngineStatus>, EngineStatus> = {
INSTALLING: 'installing',
STARTED: 'started',
STOPPED: 'stopped',
};

View file

@ -0,0 +1,56 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { entityDefinitionSchema, type EntityDefinition } from '@kbn/entities-schema';
import { ENTITY_STORE_DEFAULT_SOURCE_INDICES } from './constants';
export const HOST_ENTITY_DEFINITION: EntityDefinition = entityDefinitionSchema.parse({
id: 'ea_host_entity_store',
name: 'EA Host Store',
type: 'host',
indexPatterns: ENTITY_STORE_DEFAULT_SOURCE_INDICES,
identityFields: ['host.name'],
displayNameTemplate: '{{host.name}}',
metadata: [
'host.domain',
'host.hostname',
'host.id',
'host.ip',
'host.mac',
'host.name',
'host.type',
'host.architecture',
],
history: {
timestampField: '@timestamp',
interval: '1m',
},
version: '1.0.0',
});
export const USER_ENTITY_DEFINITION: EntityDefinition = entityDefinitionSchema.parse({
id: 'ea_user_entity_store',
name: 'EA User Store',
type: 'user',
indexPatterns: ENTITY_STORE_DEFAULT_SOURCE_INDICES,
identityFields: ['user.name'],
displayNameTemplate: '{{user.name}}',
metadata: [
'user.domain',
'user.email',
'user.full_name',
'user.hash',
'user.id',
'user.name',
'user.roles',
],
history: {
timestampField: '@timestamp',
interval: '1m',
},
version: '1.0.0',
});

View file

@ -0,0 +1,20 @@
/*
* 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 { EntityStoreDataClient } from './entity_store_data_client';
const createEntityStoreDataClientMock = () =>
({
init: jest.fn(),
start: jest.fn(),
stop: jest.fn(),
get: jest.fn(),
list: jest.fn(),
delete: jest.fn(),
} as unknown as jest.Mocked<EntityStoreDataClient>);
export const entityStoreDataClientMock = { create: createEntityStoreDataClientMock };

View file

@ -0,0 +1,120 @@
/*
* 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 { Logger, ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server';
import type { EntityClient } from '@kbn/entityManager-plugin/server/lib/entity_client';
import type {
InitEntityStoreRequestBody,
InitEntityStoreResponse,
} from '../../../../common/api/entity_analytics/entity_store/engine/init.gen';
import type {
EngineDescriptor,
EntityType,
} from '../../../../common/api/entity_analytics/entity_store/common.gen';
import { entityEngineDescriptorTypeName } from './saved_object';
import { EngineDescriptorClient } from './saved_object/engine_descriptor';
import { getEntityDefinition } from './utils/utils';
import { ENGINE_STATUS } from './constants';
interface EntityStoreClientOpts {
logger: Logger;
esClient: ElasticsearchClient;
entityClient: EntityClient;
namespace: string;
soClient: SavedObjectsClientContract;
}
export class EntityStoreDataClient {
private engineClient: EngineDescriptorClient;
constructor(private readonly options: EntityStoreClientOpts) {
this.engineClient = new EngineDescriptorClient(options.soClient);
}
public async init(
entityType: EntityType,
{ indexPattern = '', filter = '' }: InitEntityStoreRequestBody
): Promise<InitEntityStoreResponse> {
const definition = getEntityDefinition(entityType);
this.options.logger.info(`Initializing entity store for ${entityType}`);
const descriptor = await this.engineClient.init(entityType, definition, filter);
await this.options.entityClient.createEntityDefinition({
definition: {
...definition,
filter,
indexPatterns: indexPattern
? [...definition.indexPatterns, ...indexPattern.split(',')]
: definition.indexPatterns,
},
});
const updated = await this.engineClient.update(definition.id, ENGINE_STATUS.STARTED);
return { ...descriptor, ...updated };
}
public async start(entityType: EntityType) {
const definition = getEntityDefinition(entityType);
const descriptor = await this.engineClient.get(entityType);
if (descriptor.status !== ENGINE_STATUS.STOPPED) {
throw new Error(
`Cannot start Entity engine for ${entityType} when current status is: ${descriptor.status}`
);
}
this.options.logger.info(`Starting entity store for ${entityType}`);
await this.options.entityClient.startEntityDefinition(definition);
return this.engineClient.update(definition.id, ENGINE_STATUS.STARTED);
}
public async stop(entityType: EntityType) {
const definition = getEntityDefinition(entityType);
const descriptor = await this.engineClient.get(entityType);
if (descriptor.status !== ENGINE_STATUS.STARTED) {
throw new Error(
`Cannot stop Entity engine for ${entityType} when current status is: ${descriptor.status}`
);
}
this.options.logger.info(`Stopping entity store for ${entityType}`);
await this.options.entityClient.stopEntityDefinition(definition);
return this.engineClient.update(definition.id, ENGINE_STATUS.STOPPED);
}
public async get(entityType: EntityType) {
return this.engineClient.get(entityType);
}
public async list() {
return this.options.soClient
.find<EngineDescriptor>({
type: entityEngineDescriptorTypeName,
})
.then(({ saved_objects: engines }) => ({
engines: engines.map((engine) => engine.attributes),
count: engines.length,
}));
}
public async delete(entityType: EntityType, deleteData: boolean) {
const { id } = getEntityDefinition(entityType);
this.options.logger.info(`Deleting entity store for ${entityType}`);
await this.options.entityClient.deleteEntityDefinition({ id, deleteData });
await this.engineClient.delete(id);
return { deleted: true };
}
}

View file

@ -0,0 +1,64 @@
/*
* 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 { IKibanaResponse, Logger } from '@kbn/core/server';
import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils';
import { transformError } from '@kbn/securitysolution-es-utils';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import type { DeleteEntityStoreResponse } from '../../../../../common/api/entity_analytics/entity_store/engine/delete.gen';
import {
DeleteEntityStoreRequestQuery,
DeleteEntityStoreRequestParams,
} from '../../../../../common/api/entity_analytics/entity_store/engine/delete.gen';
import { API_VERSIONS, APP_ID } from '../../../../../common/constants';
import type { EntityAnalyticsRoutesDeps } from '../../types';
export const deleteEntityEngineRoute = (
router: EntityAnalyticsRoutesDeps['router'],
logger: Logger
) => {
router.versioned
.delete({
access: 'public',
path: '/api/entity_store/engines/{entityType}',
options: {
tags: ['access:securitySolution', `access:${APP_ID}-entity-analytics`],
},
})
.addVersion(
{
version: API_VERSIONS.public.v1,
validate: {
request: {
query: buildRouteValidationWithZod(DeleteEntityStoreRequestQuery),
params: buildRouteValidationWithZod(DeleteEntityStoreRequestParams),
},
},
},
async (context, request, response): Promise<IKibanaResponse<DeleteEntityStoreResponse>> => {
const siemResponse = buildSiemResponse(response);
try {
const secSol = await context.securitySolution;
const body = await secSol
.getEntityStoreDataClient()
.delete(request.params.entityType, !!request.query.data);
return response.ok({ body });
} catch (e) {
logger.error('Error in DeleteEntityStore:', e);
const error = transformError(e);
return siemResponse.error({
statusCode: error.statusCode,
body: error.message,
});
}
}
);
};

View file

@ -0,0 +1,62 @@
/*
* 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 { IKibanaResponse, Logger } from '@kbn/core/server';
import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils';
import { transformError } from '@kbn/securitysolution-es-utils';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import type { GetEntityStoreEngineResponse } from '../../../../../common/api/entity_analytics/entity_store/engine/get.gen';
import { GetEntityStoreEngineRequestParams } from '../../../../../common/api/entity_analytics/entity_store/engine/get.gen';
import { API_VERSIONS, APP_ID } from '../../../../../common/constants';
import type { EntityAnalyticsRoutesDeps } from '../../types';
export const getEntityEngineRoute = (
router: EntityAnalyticsRoutesDeps['router'],
logger: Logger
) => {
router.versioned
.get({
access: 'public',
path: '/api/entity_store/engines/{entityType}',
options: {
tags: ['access:securitySolution', `access:${APP_ID}-entity-analytics`],
},
})
.addVersion(
{
version: API_VERSIONS.public.v1,
validate: {
request: {
params: buildRouteValidationWithZod(GetEntityStoreEngineRequestParams),
},
},
},
async (
context,
request,
response
): Promise<IKibanaResponse<GetEntityStoreEngineResponse>> => {
const siemResponse = buildSiemResponse(response);
try {
const secSol = await context.securitySolution;
const body = await secSol.getEntityStoreDataClient().get(request.params.entityType);
return response.ok({ body });
} catch (e) {
logger.error('Error in GetEntityStoreEngine:', e);
const error = transformError(e);
return siemResponse.error({
statusCode: error.statusCode,
body: error.message,
});
}
}
);
};

View file

@ -0,0 +1,8 @@
/*
* 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.
*/
export { registerEntityStoreRoutes } from './register_entity_store_routes';

View file

@ -0,0 +1,65 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { IKibanaResponse, Logger } from '@kbn/core/server';
import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils';
import { transformError } from '@kbn/securitysolution-es-utils';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import type { InitEntityStoreResponse } from '../../../../../common/api/entity_analytics/entity_store/engine/init.gen';
import {
InitEntityStoreRequestBody,
InitEntityStoreRequestParams,
} from '../../../../../common/api/entity_analytics/entity_store/engine/init.gen';
import { API_VERSIONS, APP_ID } from '../../../../../common/constants';
import type { EntityAnalyticsRoutesDeps } from '../../types';
export const initEntityEngineRoute = (
router: EntityAnalyticsRoutesDeps['router'],
logger: Logger
) => {
router.versioned
.post({
access: 'public',
path: '/api/entity_store/engines/{entityType}/init',
options: {
tags: ['access:securitySolution', `access:${APP_ID}-entity-analytics`],
},
})
.addVersion(
{
version: API_VERSIONS.public.v1,
validate: {
request: {
params: buildRouteValidationWithZod(InitEntityStoreRequestParams),
body: buildRouteValidationWithZod(InitEntityStoreRequestBody),
},
},
},
async (context, request, response): Promise<IKibanaResponse<InitEntityStoreResponse>> => {
const siemResponse = buildSiemResponse(response);
try {
const secSol = await context.securitySolution;
const body: InitEntityStoreResponse = await secSol
.getEntityStoreDataClient()
.init(request.params.entityType, request.body);
return response.ok({ body });
} catch (e) {
logger.error('Error in InitEntityStore:', e);
const error = transformError(e);
return siemResponse.error({
statusCode: error.statusCode,
body: error.message,
});
}
}
);
};

View file

@ -0,0 +1,57 @@
/*
* 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 { IKibanaResponse, Logger } from '@kbn/core/server';
import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils';
import { transformError } from '@kbn/securitysolution-es-utils';
import type { ListEntityStoreEnginesResponse } from '../../../../../common/api/entity_analytics/entity_store/engine/list.gen';
import { API_VERSIONS, APP_ID } from '../../../../../common/constants';
import type { EntityAnalyticsRoutesDeps } from '../../types';
export const listEntityEnginesRoute = (
router: EntityAnalyticsRoutesDeps['router'],
logger: Logger
) => {
router.versioned
.get({
access: 'public',
path: '/api/entity_store/engines',
options: {
tags: ['access:securitySolution', `access:${APP_ID}-entity-analytics`],
},
})
.addVersion(
{
version: API_VERSIONS.public.v1,
validate: {},
},
async (
context,
request,
response
): Promise<IKibanaResponse<ListEntityStoreEnginesResponse>> => {
const siemResponse = buildSiemResponse(response);
try {
const secSol = await context.securitySolution;
const body = await secSol.getEntityStoreDataClient().list();
return response.ok({ body });
} catch (e) {
logger.error('Error in ListEntityStoreEngines:', e);
const error = transformError(e);
return siemResponse.error({
statusCode: error.statusCode,
body: error.message,
});
}
}
);
};

View file

@ -0,0 +1,23 @@
/*
* 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 { EntityAnalyticsRoutesDeps } from '../../types';
import { deleteEntityEngineRoute } from './delete';
import { getEntityEngineRoute } from './get';
import { initEntityEngineRoute } from './init';
import { listEntityEnginesRoute } from './list';
import { startEntityEngineRoute } from './start';
import { stopEntityEngineRoute } from './stop';
export const registerEntityStoreRoutes = ({ router, logger }: EntityAnalyticsRoutesDeps) => {
initEntityEngineRoute(router, logger);
startEntityEngineRoute(router, logger);
stopEntityEngineRoute(router, logger);
deleteEntityEngineRoute(router, logger);
getEntityEngineRoute(router, logger);
listEntityEnginesRoute(router, logger);
};

View file

@ -0,0 +1,59 @@
/*
* 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 { IKibanaResponse, Logger } from '@kbn/core/server';
import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils';
import { transformError } from '@kbn/securitysolution-es-utils';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import type { StartEntityStoreResponse } from '../../../../../common/api/entity_analytics/entity_store/engine/start.gen';
import { StartEntityStoreRequestParams } from '../../../../../common/api/entity_analytics/entity_store/engine/start.gen';
import { API_VERSIONS, APP_ID } from '../../../../../common/constants';
import type { EntityAnalyticsRoutesDeps } from '../../types';
import { ENGINE_STATUS } from '../constants';
export const startEntityEngineRoute = (
router: EntityAnalyticsRoutesDeps['router'],
logger: Logger
) => {
router.versioned
.post({
access: 'public',
path: '/api/entity_store/engines/{entityType}/start',
options: {
tags: ['access:securitySolution', `access:${APP_ID}-entity-analytics`],
},
})
.addVersion(
{
version: API_VERSIONS.public.v1,
validate: {
request: {
params: buildRouteValidationWithZod(StartEntityStoreRequestParams),
},
},
},
async (context, request, response): Promise<IKibanaResponse<StartEntityStoreResponse>> => {
const siemResponse = buildSiemResponse(response);
try {
const secSol = await context.securitySolution;
const engine = await secSol.getEntityStoreDataClient().start(request.params.entityType);
return response.ok({ body: { started: engine.status === ENGINE_STATUS.STARTED } });
} catch (e) {
logger.error('Error in StartEntityStore:', e);
const error = transformError(e);
return siemResponse.error({
statusCode: error.statusCode,
body: error.message,
});
}
}
);
};

View file

@ -0,0 +1,58 @@
/*
* 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 { IKibanaResponse, Logger } from '@kbn/core/server';
import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils';
import { transformError } from '@kbn/securitysolution-es-utils';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import type { GetEntityStoreStatsResponse } from '../../../../../common/api/entity_analytics/entity_store/engine/stats.gen';
import { GetEntityStoreStatsRequestParams } from '../../../../../common/api/entity_analytics/entity_store/engine/stats.gen';
import { API_VERSIONS, APP_ID } from '../../../../../common/constants';
import type { EntityAnalyticsRoutesDeps } from '../../types';
export const getEntityEngineStatsRoute = (
router: EntityAnalyticsRoutesDeps['router'],
logger: Logger
) => {
router.versioned
.post({
access: 'public',
path: '/api/entity_store/engines/{entityType}/stats',
options: {
tags: ['access:securitySolution', `access:${APP_ID}-entity-analytics`],
},
})
.addVersion(
{
version: API_VERSIONS.public.v1,
validate: {
request: {
params: buildRouteValidationWithZod(GetEntityStoreStatsRequestParams),
},
},
},
async (context, request, response): Promise<IKibanaResponse<GetEntityStoreStatsResponse>> => {
const siemResponse = buildSiemResponse(response);
try {
// TODO
throw new Error('Not implemented');
// return response.ok({ body });
} catch (e) {
logger.error('Error in GetEntityStoreStats:', e);
const error = transformError(e);
return siemResponse.error({
statusCode: error.statusCode,
body: error.message,
});
}
}
);
};

View file

@ -0,0 +1,59 @@
/*
* 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 { IKibanaResponse, Logger } from '@kbn/core/server';
import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils';
import { transformError } from '@kbn/securitysolution-es-utils';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import type { StopEntityStoreResponse } from '../../../../../common/api/entity_analytics/entity_store/engine/stop.gen';
import { StopEntityStoreRequestParams } from '../../../../../common/api/entity_analytics/entity_store/engine/stop.gen';
import { API_VERSIONS, APP_ID } from '../../../../../common/constants';
import type { EntityAnalyticsRoutesDeps } from '../../types';
import { ENGINE_STATUS } from '../constants';
export const stopEntityEngineRoute = (
router: EntityAnalyticsRoutesDeps['router'],
logger: Logger
) => {
router.versioned
.post({
access: 'public',
path: '/api/entity_store/engines/{entityType}/stop',
options: {
tags: ['access:securitySolution', `access:${APP_ID}-entity-analytics`],
},
})
.addVersion(
{
version: API_VERSIONS.public.v1,
validate: {
request: {
params: buildRouteValidationWithZod(StopEntityStoreRequestParams),
},
},
},
async (context, request, response): Promise<IKibanaResponse<StopEntityStoreResponse>> => {
const siemResponse = buildSiemResponse(response);
try {
const secSol = await context.securitySolution;
const engine = await secSol.getEntityStoreDataClient().stop(request.params.entityType);
return response.ok({ body: { stopped: engine.status === ENGINE_STATUS.STOPPED } });
} catch (e) {
logger.error('Error in StopEntityStore:', e);
const error = transformError(e);
return siemResponse.error({
statusCode: error.statusCode,
body: error.message,
});
}
}
);
};

View file

@ -0,0 +1,76 @@
/*
* 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 {
SavedObjectsClientContract,
SavedObjectsFindResponse,
} from '@kbn/core-saved-objects-api-server';
import type { EntityDefinition } from '@kbn/entities-schema';
import type {
EngineDescriptor,
EngineStatus,
EntityType,
} from '../../../../../common/api/entity_analytics/entity_store/common.gen';
import { entityEngineDescriptorTypeName } from './engine_descriptor_type';
import { getByEntityTypeQuery, getEntityDefinition } from '../utils/utils';
import { ENGINE_STATUS } from '../constants';
export class EngineDescriptorClient {
constructor(private readonly soClient: SavedObjectsClientContract) {}
async init(entityType: EntityType, definition: EntityDefinition, filter: string) {
const engineDescriptor = await this.find(entityType);
if (engineDescriptor.total > 0)
throw new Error(`Entity engine for ${entityType} already exists`);
const { attributes } = await this.soClient.create<EngineDescriptor>(
entityEngineDescriptorTypeName,
{
status: ENGINE_STATUS.INSTALLING,
type: entityType,
indexPattern: definition.indexPatterns.join(','),
filter,
},
{ id: definition.id }
);
return attributes;
}
async update(id: string, status: EngineStatus) {
const { attributes } = await this.soClient.update<EngineDescriptor>(
entityEngineDescriptorTypeName,
id,
{ status },
{ refresh: 'wait_for' }
);
return attributes;
}
async find(entityType: EntityType): Promise<SavedObjectsFindResponse<EngineDescriptor>> {
return this.soClient.find<EngineDescriptor>({
type: entityEngineDescriptorTypeName,
filter: getByEntityTypeQuery(entityType),
});
}
async get(entityType: EntityType): Promise<EngineDescriptor> {
const { id } = getEntityDefinition(entityType);
const { attributes } = await this.soClient.get<EngineDescriptor>(
entityEngineDescriptorTypeName,
id
);
return attributes;
}
async delete(id: string) {
return this.soClient.delete(entityEngineDescriptorTypeName, id);
}
}

View file

@ -0,0 +1,36 @@
/*
* 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 { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server';
import type { SavedObjectsType } from '@kbn/core/server';
export const entityEngineDescriptorTypeName = 'entity-engine-status';
export const entityEngineDescriptorTypeMappings: SavedObjectsType['mappings'] = {
dynamic: false,
properties: {
indexPattern: {
type: 'keyword',
},
filter: {
type: 'keyword',
},
type: {
type: 'keyword', // EntityType: user | host
},
status: {
type: 'keyword', // EngineStatus: installing | started | stopped
},
},
};
export const entityEngineDescriptorType: SavedObjectsType = {
name: entityEngineDescriptorTypeName,
indexPattern: SECURITY_SOLUTION_SAVED_OBJECT_INDEX,
hidden: false,
namespaceType: 'multiple-isolated',
mappings: entityEngineDescriptorTypeMappings,
};

View file

@ -0,0 +1,8 @@
/*
* 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.
*/
export * from './engine_descriptor_type';

View file

@ -0,0 +1,33 @@
/*
* 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 { SavedObjectsFindResponse } from '@kbn/core-saved-objects-api-server';
import type {
EngineDescriptor,
EntityType,
} from '../../../../../common/api/entity_analytics/entity_store/common.gen';
import { HOST_ENTITY_DEFINITION, USER_ENTITY_DEFINITION } from '../definition';
import { entityEngineDescriptorTypeName } from '../saved_object';
export const getEntityDefinition = (entityType: EntityType) => {
if (entityType === 'host') return HOST_ENTITY_DEFINITION;
if (entityType === 'user') return USER_ENTITY_DEFINITION;
throw new Error(`Unsupported entity type: ${entityType}`);
};
export const ensureEngineExists =
(entityType: EntityType) => (results: SavedObjectsFindResponse<EngineDescriptor>) => {
if (results.total === 0) {
throw new Error(`Entity engine for ${entityType} does not exist`);
}
return results.saved_objects[0].attributes;
};
export const getByEntityTypeQuery = (entityType: EntityType) => {
return `${entityEngineDescriptorTypeName}.attributes.type: ${entityType}`;
};

View file

@ -9,9 +9,13 @@ import { registerAssetCriticalityRoutes } from './asset_criticality/routes';
import { registerRiskScoreRoutes } from './risk_score/routes';
import { registerRiskEngineRoutes } from './risk_engine/routes';
import type { EntityAnalyticsRoutesDeps } from './types';
import { registerEntityStoreRoutes } from './entity_store/routes';
export const registerEntityAnalyticsRoutes = (routeDeps: EntityAnalyticsRoutesDeps) => {
registerAssetCriticalityRoutes(routeDeps);
registerRiskScoreRoutes(routeDeps);
registerRiskEngineRoutes(routeDeps);
if (routeDeps.config.experimentalFeatures.entityStoreEnabled) {
registerEntityStoreRoutes(routeDeps);
}
};

View file

@ -10,6 +10,7 @@ import { memoize } from 'lodash';
import type { Logger, KibanaRequest, RequestHandlerContext } from '@kbn/core/server';
import type { BuildFlavor } from '@kbn/config';
import { EntityClient } from '@kbn/entityManager-plugin/server/lib/entity_client';
import { DEFAULT_SPACE_ID } from '../common/constants';
import { AppClientFactory } from './client';
import type { ConfigType } from './config';
@ -31,6 +32,7 @@ import { RiskScoreDataClient } from './lib/entity_analytics/risk_score/risk_scor
import { AssetCriticalityDataClient } from './lib/entity_analytics/asset_criticality';
import { createDetectionRulesClient } from './lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client';
import { buildMlAuthz } from './lib/machine_learning/authz';
import { EntityStoreDataClient } from './lib/entity_analytics/entity_store/entity_store_data_client';
export interface IRequestContextFactory {
create(
@ -190,6 +192,22 @@ export class RequestContextFactory implements IRequestContextFactory {
auditLogger: getAuditLogger(),
})
),
getEntityStoreDataClient: memoize(() => {
const esClient = coreContext.elasticsearch.client.asCurrentUser;
const logger = options.logger;
const soClient = coreContext.savedObjects.client;
return new EntityStoreDataClient({
namespace: getSpaceId(),
esClient,
logger,
soClient,
entityClient: new EntityClient({
esClient,
soClient,
logger,
}),
});
}),
};
}
}

View file

@ -15,6 +15,7 @@ import { prebuiltRuleAssetType } from './lib/detection_engine/prebuilt_rules';
import { type as signalsMigrationType } from './lib/detection_engine/migrations/saved_objects';
import { manifestType, unifiedManifestType } from './endpoint/lib/artifacts/saved_object_mappings';
import { riskEngineConfigurationType } from './lib/entity_analytics/risk_engine/saved_object';
import { entityEngineDescriptorType } from './lib/entity_analytics/entity_store/saved_object';
const types = [
noteType,
@ -26,6 +27,7 @@ const types = [
unifiedManifestType,
signalsMigrationType,
riskEngineConfigurationType,
entityEngineDescriptorType,
protectionUpdatesNoteType,
];

View file

@ -34,6 +34,7 @@ import type { RiskEngineDataClient } from './lib/entity_analytics/risk_engine/ri
import type { RiskScoreDataClient } from './lib/entity_analytics/risk_score/risk_score_data_client';
import type { AssetCriticalityDataClient } from './lib/entity_analytics/asset_criticality';
import type { IDetectionRulesClient } from './lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client_interface';
import type { EntityStoreDataClient } from './lib/entity_analytics/entity_store/entity_store_data_client';
export { AppClient };
export interface SecuritySolutionApiRequestHandlerContext {
@ -55,6 +56,7 @@ export interface SecuritySolutionApiRequestHandlerContext {
getRiskEngineDataClient: () => RiskEngineDataClient;
getRiskScoreDataClient: () => RiskScoreDataClient;
getAssetCriticalityDataClient: () => AssetCriticalityDataClient;
getEntityStoreDataClient: () => EntityStoreDataClient;
}
export type SecuritySolutionRequestHandlerContext = CustomRequestHandlerContext<{

View file

@ -223,5 +223,7 @@
"@kbn/cloud-security-posture",
"@kbn/security-solution-distribution-bar",
"@kbn/cloud-security-posture-common",
"@kbn/entityManager-plugin",
"@kbn/entities-schema",
]
}

View file

@ -37,6 +37,10 @@ import {
CreateUpdateProtectionUpdatesNoteRequestBodyInput,
} from '@kbn/security-solution-plugin/common/api/endpoint/protection_updates_note/protection_updates_note.gen';
import { DeleteAssetCriticalityRecordRequestQueryInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/asset_criticality/delete_asset_criticality.gen';
import {
DeleteEntityStoreRequestQueryInput,
DeleteEntityStoreRequestParamsInput,
} from '@kbn/security-solution-plugin/common/api/entity_analytics/entity_store/engine/delete.gen';
import { DeleteNoteRequestBodyInput } from '@kbn/security-solution-plugin/common/api/timeline/delete_note/delete_note_route.gen';
import { DeleteRuleRequestQueryInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/crud/delete_rule/delete_rule_route.gen';
import { DeleteTimelinesRequestBodyInput } from '@kbn/security-solution-plugin/common/api/timeline/delete_timelines/delete_timelines_route.gen';
@ -76,6 +80,8 @@ import {
GetEndpointSuggestionsRequestParamsInput,
GetEndpointSuggestionsRequestBodyInput,
} from '@kbn/security-solution-plugin/common/api/endpoint/suggestions/get_suggestions.gen';
import { GetEntityStoreEngineRequestParamsInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/entity_store/engine/get.gen';
import { GetEntityStoreStatsRequestParamsInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/entity_store/engine/stats.gen';
import { GetNotesRequestQueryInput } from '@kbn/security-solution-plugin/common/api/timeline/get_notes/get_notes_route.gen';
import { GetPolicyResponseRequestQueryInput } from '@kbn/security-solution-plugin/common/api/endpoint/policy/policy_response.gen';
import { GetProtectionUpdatesNoteRequestParamsInput } from '@kbn/security-solution-plugin/common/api/endpoint/protection_updates_note/protection_updates_note.gen';
@ -91,6 +97,10 @@ import { GetTimelineRequestQueryInput } from '@kbn/security-solution-plugin/comm
import { GetTimelinesRequestQueryInput } from '@kbn/security-solution-plugin/common/api/timeline/get_timelines/get_timelines_route.gen';
import { ImportRulesRequestQueryInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/import_rules/import_rules_route.gen';
import { ImportTimelinesRequestBodyInput } from '@kbn/security-solution-plugin/common/api/timeline/import_timelines/import_timelines_route.gen';
import {
InitEntityStoreRequestParamsInput,
InitEntityStoreRequestBodyInput,
} from '@kbn/security-solution-plugin/common/api/entity_analytics/entity_store/engine/init.gen';
import { InstallPrepackedTimelinesRequestBodyInput } from '@kbn/security-solution-plugin/common/api/timeline/install_prepackaged_timelines/install_prepackaged_timelines_route.gen';
import { PatchRuleRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/crud/patch_rule/patch_rule_route.gen';
import { PatchTimelineRequestBodyInput } from '@kbn/security-solution-plugin/common/api/timeline/patch_timelines/patch_timeline_route.gen';
@ -110,6 +120,8 @@ import { SearchAlertsRequestBodyInput } from '@kbn/security-solution-plugin/comm
import { SetAlertAssigneesRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/alert_assignees/set_alert_assignees_route.gen';
import { SetAlertsStatusRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/signals/set_signal_status/set_signals_status_route.gen';
import { SetAlertTagsRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags.gen';
import { StartEntityStoreRequestParamsInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/entity_store/engine/start.gen';
import { StopEntityStoreRequestParamsInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/entity_store/engine/stop.gen';
import { SuggestUserProfilesRequestQueryInput } from '@kbn/security-solution-plugin/common/api/detection_engine/users/suggest_user_profiles_route.gen';
import { TriggerRiskScoreCalculationRequestBodyInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/risk_engine/entity_calculation_route.gen';
import { UpdateRuleRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/crud/update_rule/update_rule_route.gen';
@ -313,6 +325,14 @@ Migrations are initiated per index. While the process is neither destructive nor
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
.query(props.query);
},
deleteEntityStore(props: DeleteEntityStoreProps) {
return supertest
.delete(replaceParams('/api/entity_store/engines/{entityType}', props.params))
.set('kbn-xsrf', 'true')
.set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31')
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
.query(props.query);
},
deleteNote(props: DeleteNoteProps) {
return supertest
.delete('/api/note')
@ -668,6 +688,20 @@ finalize it.
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
.send(props.body as object);
},
getEntityStoreEngine(props: GetEntityStoreEngineProps) {
return supertest
.get(replaceParams('/api/entity_store/engines/{entityType}', props.params))
.set('kbn-xsrf', 'true')
.set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31')
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana');
},
getEntityStoreStats(props: GetEntityStoreStatsProps) {
return supertest
.post(replaceParams('/api/entity_store/engines/{entityType}/stats', props.params))
.set('kbn-xsrf', 'true')
.set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31')
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana');
},
/**
* Gets notes
*/
@ -764,6 +798,14 @@ finalize it.
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
.send(props.body as object);
},
initEntityStore(props: InitEntityStoreProps) {
return supertest
.post(replaceParams('/api/entity_store/engines/{entityType}/init', props.params))
.set('kbn-xsrf', 'true')
.set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31')
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
.send(props.body as object);
},
/**
* Initializes the Risk Engine by creating the necessary indices and mappings, removing old transforms, and starting the new risk engine
*/
@ -799,6 +841,13 @@ finalize it.
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana');
},
listEntityStoreEngines() {
return supertest
.get('/api/entity_store/engines')
.set('kbn-xsrf', 'true')
.set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31')
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana');
},
/**
* Update specific fields of an existing detection rule using the `rule_id` or `id` field.
*/
@ -1018,6 +1067,20 @@ detection engine rules.
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
.send(props.body as object);
},
startEntityStore(props: StartEntityStoreProps) {
return supertest
.post(replaceParams('/api/entity_store/engines/{entityType}/start', props.params))
.set('kbn-xsrf', 'true')
.set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31')
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana');
},
stopEntityStore(props: StopEntityStoreProps) {
return supertest
.post(replaceParams('/api/entity_store/engines/{entityType}/stop', props.params))
.set('kbn-xsrf', 'true')
.set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31')
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana');
},
/**
* Suggests user profiles.
*/
@ -1107,6 +1170,10 @@ export interface CreateUpdateProtectionUpdatesNoteProps {
export interface DeleteAssetCriticalityRecordProps {
query: DeleteAssetCriticalityRecordRequestQueryInput;
}
export interface DeleteEntityStoreProps {
query: DeleteEntityStoreRequestQueryInput;
params: DeleteEntityStoreRequestParamsInput;
}
export interface DeleteNoteProps {
body: DeleteNoteRequestBodyInput;
}
@ -1200,6 +1267,12 @@ export interface GetEndpointSuggestionsProps {
params: GetEndpointSuggestionsRequestParamsInput;
body: GetEndpointSuggestionsRequestBodyInput;
}
export interface GetEntityStoreEngineProps {
params: GetEntityStoreEngineRequestParamsInput;
}
export interface GetEntityStoreStatsProps {
params: GetEntityStoreStatsRequestParamsInput;
}
export interface GetNotesProps {
query: GetNotesRequestQueryInput;
}
@ -1229,6 +1302,10 @@ export interface ImportRulesProps {
export interface ImportTimelinesProps {
body: ImportTimelinesRequestBodyInput;
}
export interface InitEntityStoreProps {
params: InitEntityStoreRequestParamsInput;
body: InitEntityStoreRequestBodyInput;
}
export interface InstallPrepackedTimelinesProps {
body: InstallPrepackedTimelinesRequestBodyInput;
}
@ -1278,6 +1355,12 @@ export interface SetAlertsStatusProps {
export interface SetAlertTagsProps {
body: SetAlertTagsRequestBodyInput;
}
export interface StartEntityStoreProps {
params: StartEntityStoreRequestParamsInput;
}
export interface StopEntityStoreProps {
params: StopEntityStoreRequestParamsInput;
}
export interface SuggestUserProfilesProps {
query: SuggestUserProfilesRequestQueryInput;
}

View file

@ -349,6 +349,18 @@ export default function ({ getService }: FtrProviderContext) {
"saved_object:risk-engine-configuration/delete",
"saved_object:risk-engine-configuration/bulk_delete",
"saved_object:risk-engine-configuration/share_to_space",
"saved_object:entity-engine-status/bulk_get",
"saved_object:entity-engine-status/get",
"saved_object:entity-engine-status/find",
"saved_object:entity-engine-status/open_point_in_time",
"saved_object:entity-engine-status/close_point_in_time",
"saved_object:entity-engine-status/create",
"saved_object:entity-engine-status/bulk_create",
"saved_object:entity-engine-status/update",
"saved_object:entity-engine-status/bulk_update",
"saved_object:entity-engine-status/delete",
"saved_object:entity-engine-status/bulk_delete",
"saved_object:entity-engine-status/share_to_space",
"saved_object:policy-settings-protection-updates-note/bulk_get",
"saved_object:policy-settings-protection-updates-note/get",
"saved_object:policy-settings-protection-updates-note/find",
@ -1182,6 +1194,18 @@ export default function ({ getService }: FtrProviderContext) {
"saved_object:risk-engine-configuration/delete",
"saved_object:risk-engine-configuration/bulk_delete",
"saved_object:risk-engine-configuration/share_to_space",
"saved_object:entity-engine-status/bulk_get",
"saved_object:entity-engine-status/get",
"saved_object:entity-engine-status/find",
"saved_object:entity-engine-status/open_point_in_time",
"saved_object:entity-engine-status/close_point_in_time",
"saved_object:entity-engine-status/create",
"saved_object:entity-engine-status/bulk_create",
"saved_object:entity-engine-status/update",
"saved_object:entity-engine-status/bulk_update",
"saved_object:entity-engine-status/delete",
"saved_object:entity-engine-status/bulk_delete",
"saved_object:entity-engine-status/share_to_space",
"saved_object:policy-settings-protection-updates-note/bulk_get",
"saved_object:policy-settings-protection-updates-note/get",
"saved_object:policy-settings-protection-updates-note/find",
@ -1779,6 +1803,11 @@ export default function ({ getService }: FtrProviderContext) {
"saved_object:risk-engine-configuration/find",
"saved_object:risk-engine-configuration/open_point_in_time",
"saved_object:risk-engine-configuration/close_point_in_time",
"saved_object:entity-engine-status/bulk_get",
"saved_object:entity-engine-status/get",
"saved_object:entity-engine-status/find",
"saved_object:entity-engine-status/open_point_in_time",
"saved_object:entity-engine-status/close_point_in_time",
"saved_object:policy-settings-protection-updates-note/bulk_get",
"saved_object:policy-settings-protection-updates-note/get",
"saved_object:policy-settings-protection-updates-note/find",
@ -2135,6 +2164,11 @@ export default function ({ getService }: FtrProviderContext) {
"saved_object:risk-engine-configuration/find",
"saved_object:risk-engine-configuration/open_point_in_time",
"saved_object:risk-engine-configuration/close_point_in_time",
"saved_object:entity-engine-status/bulk_get",
"saved_object:entity-engine-status/get",
"saved_object:entity-engine-status/find",
"saved_object:entity-engine-status/open_point_in_time",
"saved_object:entity-engine-status/close_point_in_time",
"saved_object:policy-settings-protection-updates-note/bulk_get",
"saved_object:policy-settings-protection-updates-note/get",
"saved_object:policy-settings-protection-updates-note/find",