mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Security Solution] add blocklist list (#126390)
This commit is contained in:
parent
e72dafc038
commit
4e3d2f62fd
11 changed files with 285 additions and 9 deletions
|
@ -14,6 +14,7 @@ export const exceptionListType = t.keyof({
|
|||
endpoint_trusted_apps: null,
|
||||
endpoint_events: null,
|
||||
endpoint_host_isolation_exceptions: null,
|
||||
endpoint_blocklists: null,
|
||||
});
|
||||
export const exceptionListTypeOrUndefined = t.union([exceptionListType, t.undefined]);
|
||||
export type ExceptionListType = t.TypeOf<typeof exceptionListType>;
|
||||
|
@ -24,4 +25,5 @@ export enum ExceptionListTypeEnum {
|
|||
ENDPOINT_TRUSTED_APPS = 'endpoint',
|
||||
ENDPOINT_EVENTS = 'endpoint_events',
|
||||
ENDPOINT_HOST_ISOLATION_EXCEPTIONS = 'endpoint_host_isolation_exceptions',
|
||||
ENDPOINT_BLOCKLISTS = 'endpoint_blocklists',
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ describe('Lists', () => {
|
|||
const message = pipe(decoded, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
'Invalid value "1" supplied to "Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "endpoint" | "endpoint_trusted_apps" | "endpoint_events" | "endpoint_host_isolation_exceptions", namespace_type: "agnostic" | "single" |}>"',
|
||||
'Invalid value "1" supplied to "Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "endpoint" | "endpoint_trusted_apps" | "endpoint_events" | "endpoint_host_isolation_exceptions" | "endpoint_blocklists", namespace_type: "agnostic" | "single" |}>"',
|
||||
]);
|
||||
expect(message.schema).toEqual({});
|
||||
});
|
||||
|
@ -117,8 +117,8 @@ describe('Lists', () => {
|
|||
const message = pipe(decoded, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
'Invalid value "1" supplied to "(Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "endpoint" | "endpoint_trusted_apps" | "endpoint_events" | "endpoint_host_isolation_exceptions", namespace_type: "agnostic" | "single" |}> | undefined)"',
|
||||
'Invalid value "[1]" supplied to "(Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "endpoint" | "endpoint_trusted_apps" | "endpoint_events" | "endpoint_host_isolation_exceptions", namespace_type: "agnostic" | "single" |}> | undefined)"',
|
||||
'Invalid value "1" supplied to "(Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "endpoint" | "endpoint_trusted_apps" | "endpoint_events" | "endpoint_host_isolation_exceptions" | "endpoint_blocklists", namespace_type: "agnostic" | "single" |}> | undefined)"',
|
||||
'Invalid value "[1]" supplied to "(Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "endpoint" | "endpoint_trusted_apps" | "endpoint_events" | "endpoint_host_isolation_exceptions" | "endpoint_blocklists", namespace_type: "agnostic" | "single" |}> | undefined)"',
|
||||
]);
|
||||
expect(message.schema).toEqual({});
|
||||
});
|
||||
|
|
|
@ -76,3 +76,7 @@ export const ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_NAME =
|
|||
'Endpoint Security Host isolation exceptions List';
|
||||
export const ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_DESCRIPTION =
|
||||
'Endpoint Security Host isolation exceptions List';
|
||||
|
||||
export const ENDPOINT_BLOCKLISTS_LIST_ID = 'endpoint_blocklists';
|
||||
export const ENDPOINT_BLOCKLISTS_LIST_NAME = 'Endpoint Security Blocklists List';
|
||||
export const ENDPOINT_BLOCKLISTS_LIST_DESCRIPTION = 'Endpoint Security Blocklists List';
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
ENDPOINT_EVENT_FILTERS_LIST_ID,
|
||||
ENDPOINT_TRUSTED_APPS_LIST_ID,
|
||||
ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID,
|
||||
ENDPOINT_BLOCKLISTS_LIST_ID,
|
||||
} from '@kbn/securitysolution-list-constants';
|
||||
import { BaseDataGenerator } from './base_data_generator';
|
||||
import { ConditionEntryField } from '../types';
|
||||
|
@ -250,4 +251,70 @@ export class ExceptionsListItemGenerator extends BaseDataGenerator<ExceptionList
|
|||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
generateBlocklist(overrides: Partial<ExceptionListItemSchema> = {}): ExceptionListItemSchema {
|
||||
return this.generate({
|
||||
name: `Blocklist ${this.randomString(5)}`,
|
||||
list_id: ENDPOINT_BLOCKLISTS_LIST_ID,
|
||||
item_id: `generator_endpoint_blocklist_${this.randomUUID()}`,
|
||||
os_types: ['windows'],
|
||||
entries: [
|
||||
this.randomChoice([
|
||||
{
|
||||
field: 'process.executable.caseless',
|
||||
value: ['/some/path', 'some/other/path', 'yet/another/path'],
|
||||
type: 'match_any',
|
||||
operator: 'included',
|
||||
},
|
||||
{
|
||||
field: 'process.hash.sha256',
|
||||
value: [
|
||||
'a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3',
|
||||
'2C26B46B68FFC68FF99B453C1D30413413422D706483BFA0F98A5E886266E7AE',
|
||||
'FCDE2B2EDBA56BF408601FB721FE9B5C338D10EE429EA04FAE5511B68FBF8FB9',
|
||||
],
|
||||
type: 'match_any',
|
||||
operator: 'included',
|
||||
},
|
||||
{
|
||||
field: 'process.Ext.code_signature',
|
||||
entries: [
|
||||
{
|
||||
field: 'trusted',
|
||||
value: 'true',
|
||||
type: 'match',
|
||||
operator: 'included',
|
||||
},
|
||||
{
|
||||
field: 'subject_name',
|
||||
value: ['notsus.exe', 'verynotsus.exe', 'superlegit.exe'],
|
||||
type: 'match_any',
|
||||
operator: 'included',
|
||||
},
|
||||
],
|
||||
type: 'nested',
|
||||
},
|
||||
]),
|
||||
],
|
||||
...overrides,
|
||||
});
|
||||
}
|
||||
|
||||
generateBlocklistForCreate(
|
||||
overrides: Partial<CreateExceptionListItemSchema> = {}
|
||||
): CreateExceptionListItemSchemaWithNonNullProps {
|
||||
return {
|
||||
...exceptionItemToCreateExceptionItem(this.generateBlocklist()),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
generateBlocklistForUpdate(
|
||||
overrides: Partial<UpdateExceptionListItemSchema> = {}
|
||||
): UpdateExceptionListItemSchemaWithNonNullProps {
|
||||
return {
|
||||
...exceptionItemToUpdateExceptionItem(this.generateBlocklist()),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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 {
|
||||
ExceptionListType,
|
||||
ExceptionListTypeEnum,
|
||||
CreateExceptionListSchema,
|
||||
} from '@kbn/securitysolution-io-ts-list-types';
|
||||
import {
|
||||
ENDPOINT_BLOCKLISTS_LIST_DESCRIPTION,
|
||||
ENDPOINT_BLOCKLISTS_LIST_ID,
|
||||
ENDPOINT_BLOCKLISTS_LIST_NAME,
|
||||
} from '@kbn/securitysolution-list-constants';
|
||||
|
||||
export const BLOCKLISTS_LIST_TYPE: ExceptionListType = ExceptionListTypeEnum.ENDPOINT_BLOCKLISTS;
|
||||
|
||||
export const BLOCKLISTS_LIST_DEFINITION: CreateExceptionListSchema = {
|
||||
name: ENDPOINT_BLOCKLISTS_LIST_NAME,
|
||||
namespace_type: 'agnostic',
|
||||
description: ENDPOINT_BLOCKLISTS_LIST_DESCRIPTION,
|
||||
list_id: ENDPOINT_BLOCKLISTS_LIST_ID,
|
||||
type: BLOCKLISTS_LIST_TYPE,
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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 { ENDPOINT_BLOCKLISTS_LIST_ID } from '@kbn/securitysolution-list-constants';
|
||||
import { HttpStart } from 'kibana/public';
|
||||
import { ExceptionsListApiClient } from '../../../services/exceptions_list/exceptions_list_api_client';
|
||||
import { BLOCKLISTS_LIST_DEFINITION } from '../constants';
|
||||
|
||||
/**
|
||||
* Blocklist exceptions Api client class using ExceptionsListApiClient as base class
|
||||
* It follow the Singleton pattern.
|
||||
* Please, use the getInstance method instead of creating a new instance when using this implementation.
|
||||
*/
|
||||
export class BlocklistsApiClient extends ExceptionsListApiClient {
|
||||
constructor(http: HttpStart) {
|
||||
super(http, ENDPOINT_BLOCKLISTS_LIST_ID, BLOCKLISTS_LIST_DEFINITION);
|
||||
}
|
||||
|
||||
public static getInstance(http: HttpStart): ExceptionsListApiClient {
|
||||
return super.getInstance(http, ENDPOINT_BLOCKLISTS_LIST_ID, BLOCKLISTS_LIST_DEFINITION);
|
||||
}
|
||||
}
|
|
@ -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 './blocklists_api_client';
|
|
@ -9,7 +9,7 @@ import React, { memo } from 'react';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { useHttp } from '../../../../common/lib/kibana';
|
||||
import { ArtifactListPage, ArtifactListPageProps } from '../../../components/artifact_list_page';
|
||||
import { HostIsolationExceptionsApiClient } from '../../host_isolation_exceptions/host_isolation_exceptions_api_client';
|
||||
import { BlocklistsApiClient } from '../services';
|
||||
|
||||
// FIXME:PT delete this when real component is implemented
|
||||
const TempDevFormComponent: ArtifactListPageProps['ArtifactFormComponent'] = (props) => {
|
||||
|
@ -39,7 +39,7 @@ const BLOCKLIST_PAGE_LABELS: ArtifactListPageProps['labels'] = {
|
|||
defaultMessage: 'Blocklist',
|
||||
}),
|
||||
pageAboutInfo: i18n.translate('xpack.securitySolution.blocklist.pageAboutInfo', {
|
||||
defaultMessage: '(DEV: temporarily using isolation exception api)', // FIXME: need wording from PM
|
||||
defaultMessage: 'Add a blocklist to block applications or files from running.',
|
||||
}),
|
||||
pageAddButtonTitle: i18n.translate('xpack.securitySolution.blocklist.pageAddButtonTitle', {
|
||||
defaultMessage: 'Add blocklist entry',
|
||||
|
@ -118,13 +118,11 @@ const BLOCKLIST_PAGE_LABELS: ArtifactListPageProps['labels'] = {
|
|||
|
||||
export const Blocklist = memo(() => {
|
||||
const http = useHttp();
|
||||
// FIXME: Implement Blocklist API client and define list
|
||||
// for now, just using Event Filters
|
||||
const eventFiltersApiClient = HostIsolationExceptionsApiClient.getInstance(http);
|
||||
const blocklistsApiClient = BlocklistsApiClient.getInstance(http);
|
||||
|
||||
return (
|
||||
<ArtifactListPage
|
||||
apiClient={eventFiltersApiClient}
|
||||
apiClient={blocklistsApiClient}
|
||||
ArtifactFormComponent={TempDevFormComponent} // FIXME: Implement create/edit form
|
||||
labels={BLOCKLIST_PAGE_LABELS}
|
||||
data-test-subj="blocklistPage"
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* 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 { run, RunFn, createFailError } from '@kbn/dev-utils';
|
||||
import { KbnClient } from '@kbn/test';
|
||||
import { AxiosError } from 'axios';
|
||||
import pMap from 'p-map';
|
||||
import type { CreateExceptionListSchema } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import {
|
||||
ENDPOINT_BLOCKLISTS_LIST_DESCRIPTION,
|
||||
ENDPOINT_BLOCKLISTS_LIST_ID,
|
||||
ENDPOINT_BLOCKLISTS_LIST_NAME,
|
||||
EXCEPTION_LIST_ITEM_URL,
|
||||
EXCEPTION_LIST_URL,
|
||||
} from '@kbn/securitysolution-list-constants';
|
||||
import { randomPolicyIdGenerator } from '../common/random_policy_id_generator';
|
||||
import { ExceptionsListItemGenerator } from '../../../common/endpoint/data_generators/exceptions_list_item_generator';
|
||||
import { isArtifactByPolicy } from '../../../common/endpoint/service/artifacts';
|
||||
|
||||
export const cli = () => {
|
||||
run(
|
||||
async (options) => {
|
||||
try {
|
||||
await createBlocklists(options);
|
||||
options.log.success(`${options.flags.count} endpoint blocklists created`);
|
||||
} catch (e) {
|
||||
options.log.error(e);
|
||||
throw createFailError(e.message);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'Load Endpoint Blocklists',
|
||||
flags: {
|
||||
string: ['kibana'],
|
||||
default: {
|
||||
count: 10,
|
||||
kibana: 'http://elastic:changeme@localhost:5601',
|
||||
},
|
||||
help: `
|
||||
--count Number of blocklists to create. Default: 10
|
||||
--kibana The URL to kibana including credentials. Default: http://elastic:changeme@localhost:5601
|
||||
`,
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
class BlocklistDataLoaderError extends Error {
|
||||
constructor(message: string, public readonly meta: unknown) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
const handleThrowAxiosHttpError = (err: AxiosError): never => {
|
||||
let message = err.message;
|
||||
|
||||
if (err.response) {
|
||||
message = `[${err.response.status}] ${err.response.data.message ?? err.message} [ ${String(
|
||||
err.response.config.method
|
||||
).toUpperCase()} ${err.response.config.url} ]`;
|
||||
}
|
||||
throw new BlocklistDataLoaderError(message, err.toJSON());
|
||||
};
|
||||
|
||||
const createBlocklists: RunFn = async ({ flags, log }) => {
|
||||
const eventGenerator = new ExceptionsListItemGenerator();
|
||||
const kbn = new KbnClient({ log, url: flags.kibana as string });
|
||||
|
||||
await ensureCreateEndpointBlocklistsList(kbn);
|
||||
|
||||
const randomPolicyId = await randomPolicyIdGenerator(kbn, log);
|
||||
|
||||
await pMap(
|
||||
Array.from({ length: flags.count as unknown as number }),
|
||||
() => {
|
||||
const body = eventGenerator.generateBlocklistForCreate();
|
||||
|
||||
if (isArtifactByPolicy(body)) {
|
||||
const nmExceptions = Math.floor(Math.random() * 3) || 1;
|
||||
body.tags = Array.from({ length: nmExceptions }, () => {
|
||||
return `policy:${randomPolicyId()}`;
|
||||
});
|
||||
}
|
||||
return kbn
|
||||
.request({
|
||||
method: 'POST',
|
||||
path: EXCEPTION_LIST_ITEM_URL,
|
||||
body,
|
||||
})
|
||||
.catch((e) => handleThrowAxiosHttpError(e));
|
||||
},
|
||||
{ concurrency: 10 }
|
||||
);
|
||||
};
|
||||
|
||||
const ensureCreateEndpointBlocklistsList = async (kbn: KbnClient) => {
|
||||
const newListDefinition: CreateExceptionListSchema = {
|
||||
description: ENDPOINT_BLOCKLISTS_LIST_DESCRIPTION,
|
||||
list_id: ENDPOINT_BLOCKLISTS_LIST_ID,
|
||||
meta: undefined,
|
||||
name: ENDPOINT_BLOCKLISTS_LIST_NAME,
|
||||
os_types: [],
|
||||
tags: [],
|
||||
type: 'endpoint',
|
||||
namespace_type: 'agnostic',
|
||||
};
|
||||
|
||||
await kbn
|
||||
.request({
|
||||
method: 'POST',
|
||||
path: EXCEPTION_LIST_URL,
|
||||
body: newListDefinition,
|
||||
})
|
||||
.catch((e) => {
|
||||
// Ignore if list was already created
|
||||
if (e.response.status !== 409) {
|
||||
handleThrowAxiosHttpError(e);
|
||||
}
|
||||
});
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
require('../../../../../src/setup_node_env');
|
||||
require('./blocklists').cli();
|
|
@ -18,6 +18,7 @@ import { TRUSTED_APPS_EXCEPTION_LIST_DEFINITION } from '../../../plugins/securit
|
|||
import { EndpointError } from '../../../plugins/security_solution/common/endpoint/errors';
|
||||
import { EVENT_FILTER_LIST_DEFINITION } from '../../../plugins/security_solution/public/management/pages/event_filters/constants';
|
||||
import { HOST_ISOLATION_EXCEPTIONS_LIST_DEFINITION } from '../../../plugins/security_solution/public/management/pages/host_isolation_exceptions/constants';
|
||||
import { BLOCKLISTS_LIST_DEFINITION } from '../../../plugins/security_solution/public/management/pages/blocklist/constants';
|
||||
|
||||
export interface ArtifactTestData {
|
||||
artifact: ExceptionListItemSchema;
|
||||
|
@ -108,4 +109,13 @@ export class EndpointArtifactsTestResources extends FtrService {
|
|||
|
||||
return this.createExceptionItem(artifact);
|
||||
}
|
||||
|
||||
async createBlocklist(
|
||||
overrides: Partial<CreateExceptionListItemSchema> = {}
|
||||
): Promise<ArtifactTestData> {
|
||||
await this.ensureListExists(BLOCKLISTS_LIST_DEFINITION);
|
||||
const blocklist = this.exceptionsGenerator.generateBlocklistForCreate(overrides);
|
||||
|
||||
return this.createExceptionItem(blocklist);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue