Skip to content

Commit

Permalink
[Security Solution] workflow insights service CRUD methods (elastic#2…
Browse files Browse the repository at this point in the history
…01724)

## Summary

Add implementation of create, update, and fetch methods for the
`SecurityWorkflowInsightsService`.


### Checklist

- [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

Co-authored-by: Konrad Szwarc <[email protected]>
  • Loading branch information
joeypoon and szwarckonrad authored Nov 29, 2024
1 parent 82de734 commit 2ba067e
Show file tree
Hide file tree
Showing 6 changed files with 419 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* 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 { Moment } from 'moment';

import type { DefendInsightType } from '@kbn/elastic-assistant-common';
import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';

export enum Category {
Endpoint = 'endpoint',
}

export enum SourceType {
LlmConnector = 'llm-connector',
}

export enum TargetType {
Endpoint = 'endpoint',
}

export enum ActionType {
Refreshed = 'refreshed', // new or refreshed
Remediated = 'remediated',
Suppressed = 'suppressed', // temporarily supressed, can be refreshed
Dismissed = 'dismissed', // "permanently" dismissed, cannot be normally refreshed
}

export type ExceptionListRemediationType = Pick<
ExceptionListItemSchema,
'list_id' | 'name' | 'description' | 'entries' | 'tags' | 'os_types'
>;

export interface SecurityWorkflowInsight {
id?: string;
'@timestamp': Moment;
message: string;
category: Category;
type: DefendInsightType;
source: {
type: SourceType;
id: string;
data_range_start: Moment;
data_range_end: Moment;
};
target: {
type: TargetType;
ids: string[];
};
action: {
type: ActionType;
timestamp: Moment;
};
value: string;
remediation: {
exception_list_items?: ExceptionListRemediationType[];
};
metadata: {
notes?: Record<string, string>;
message_variables?: string[];
};
}

export interface SearchParams {
size?: number;
from?: number;
ids?: string[];
categories?: Category[];
types?: DefendInsightType[];
sourceTypes?: SourceType[];
sourceIds?: string[];
targetTypes?: TargetType[];
targetIds?: string[];
actionTypes: ActionType[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

export const DATA_STREAM_PREFIX = '.security-workflow-insights';
export const DATA_STREAM_PREFIX = '.edr-workflow-insights';
export const COMPONENT_TEMPLATE_NAME = `${DATA_STREAM_PREFIX}-component-template`;
export const INDEX_TEMPLATE_NAME = `${DATA_STREAM_PREFIX}-index-template`;
export const INGEST_PIPELINE_NAME = `${DATA_STREAM_PREFIX}-ingest-pipeline`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@
*/

import type { ElasticsearchClient } from '@kbn/core/server';
import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks';

import { DataStreamSpacesAdapter } from '@kbn/data-stream-adapter';
import { DefendInsightType } from '@kbn/elastic-assistant-common';
import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks';
import { kibanaPackageJson } from '@kbn/repo-info';

import { createDatastream, createPipeline } from './helpers';
import type { SearchParams } from '../../../../common/endpoint/types/workflow_insights';

import { buildEsQueryParams, createDatastream, createPipeline } from './helpers';
import {
DATA_STREAM_PREFIX,
COMPONENT_TEMPLATE_NAME,
Expand All @@ -19,6 +23,12 @@ import {
TOTAL_FIELDS_LIMIT,
} from './constants';
import { securityWorkflowInsightsFieldMap } from './field_map_configurations';
import {
ActionType,
Category,
SourceType,
TargetType,
} from '../../../../common/endpoint/types/workflow_insights';

jest.mock('@kbn/data-stream-adapter', () => ({
DataStreamSpacesAdapter: jest.fn().mockImplementation(() => ({
Expand Down Expand Up @@ -77,4 +87,64 @@ describe('helpers', () => {
});
});
});

describe('buildEsQueryParams', () => {
it('should build es query correct', () => {
const searchParams: SearchParams = {
size: 50,
from: 50,
ids: ['id1', 'id2'],
categories: [Category.Endpoint],
types: [DefendInsightType.Enum.incompatible_antivirus],
sourceTypes: [SourceType.LlmConnector],
sourceIds: ['source-id1', 'source-id2'],
targetTypes: [TargetType.Endpoint],
targetIds: ['target-id1', 'target-id2'],
actionTypes: [ActionType.Refreshed, ActionType.Remediated],
};
const result = buildEsQueryParams(searchParams);
expect(result).toEqual([
{
terms: {
_id: ['id1', 'id2'],
},
},
{
terms: {
categories: ['endpoint'],
},
},
{
terms: {
types: ['incompatible_antivirus'],
},
},
{
terms: {
'source.type': ['llm-connector'],
},
},
{
terms: {
'source.id': ['source-id1', 'source-id2'],
},
},
{
terms: {
'target.type': ['endpoint'],
},
},
{
terms: {
'target.id': ['target-id1', 'target-id2'],
},
},
{
terms: {
'action.type': ['refreshed', 'remediated'],
},
},
]);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@
* 2.0.
*/

import { get as _get } from 'lodash';

import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
import type { ElasticsearchClient } from '@kbn/core/server';

import { DataStreamSpacesAdapter } from '@kbn/data-stream-adapter';

import type { SearchParams } from '../../../../common/endpoint/types/workflow_insights';

import {
COMPONENT_TEMPLATE_NAME,
DATA_STREAM_PREFIX,
Expand Down Expand Up @@ -60,3 +65,34 @@ export async function createPipeline(esClient: ElasticsearchClient): Promise<boo
});
return response.acknowledged;
}

const validKeys = new Set([
'ids',
'categories',
'types',
'sourceTypes',
'sourceIds',
'targetTypes',
'targetIds',
'actionTypes',
]);
const paramFieldMap = {
ids: '_id',
sourceTypes: 'source.type',
sourceIds: 'source.id',
targetTypes: 'target.type',
targetIds: 'target.id',
actionTypes: 'action.type',
};
export function buildEsQueryParams(searchParams: SearchParams): QueryDslQueryContainer[] {
return Object.entries(searchParams).reduce((acc: object[], [k, v]) => {
if (!validKeys.has(k)) {
return acc;
}

const paramKey = _get(paramFieldMap, k, k);
const next = { terms: { [paramKey]: v } };

return [...acc, next];
}, []);
}
Loading

0 comments on commit 2ba067e

Please sign in to comment.