mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[SECURITY_SOLUTION][ENDPOINT] Trusted Apps List API (#75476)
* Trusted Apps initial setup for route registration * Added types for TrustedApp entries * trusted apps list API returns results * use methods and const from latest PR merge to lists * a quick generator for trusted apps entries * support cli options for trusted app data loader * Add mocked `createTrustedAppsList()` method to `ExceptionListClientMock` * tests fro trusted apps route handlers * tests for trusted apps schema * Correct name of mock method * Fix service to ensure return value of `getExceptionList` service throws if service not available * Fix types * Refactor TrustedApp type + code review feedback
This commit is contained in:
parent
532f2d70e8
commit
9873df8ee0
16 changed files with 503 additions and 3 deletions
|
@ -19,6 +19,11 @@ import {
|
|||
_VERSION,
|
||||
} from '../../constants.mock';
|
||||
import { ENDPOINT_LIST_ID } from '../..';
|
||||
import {
|
||||
ENDPOINT_TRUSTED_APPS_LIST_DESCRIPTION,
|
||||
ENDPOINT_TRUSTED_APPS_LIST_ID,
|
||||
ENDPOINT_TRUSTED_APPS_LIST_NAME,
|
||||
} from '../../constants';
|
||||
|
||||
import { ExceptionListSchema } from './exception_list_schema';
|
||||
|
||||
|
@ -42,6 +47,15 @@ export const getExceptionListSchemaMock = (): ExceptionListSchema => ({
|
|||
version: VERSION,
|
||||
});
|
||||
|
||||
export const getTrustedAppsListSchemaMock = (): ExceptionListSchema => {
|
||||
return {
|
||||
...getExceptionListSchemaMock(),
|
||||
description: ENDPOINT_TRUSTED_APPS_LIST_DESCRIPTION,
|
||||
list_id: ENDPOINT_TRUSTED_APPS_LIST_ID,
|
||||
name: ENDPOINT_TRUSTED_APPS_LIST_NAME,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* This is useful for end to end tests where we remove the auto generated parts for comparisons
|
||||
* such as created_at, updated_at, and id.
|
||||
|
|
|
@ -9,7 +9,10 @@ import { savedObjectsClientMock } from 'src/core/server/mocks';
|
|||
import { getFoundExceptionListSchemaMock } from '../../../common/schemas/response/found_exception_list_schema.mock';
|
||||
import { getFoundExceptionListItemSchemaMock } from '../../../common/schemas/response/found_exception_list_item_schema.mock';
|
||||
import { getExceptionListItemSchemaMock } from '../../../common/schemas/response/exception_list_item_schema.mock';
|
||||
import { getExceptionListSchemaMock } from '../../../common/schemas/response/exception_list_schema.mock';
|
||||
import {
|
||||
getExceptionListSchemaMock,
|
||||
getTrustedAppsListSchemaMock,
|
||||
} from '../../../common/schemas/response/exception_list_schema.mock';
|
||||
|
||||
import { ExceptionListClient } from './exception_list_client';
|
||||
|
||||
|
@ -24,6 +27,7 @@ export class ExceptionListClientMock extends ExceptionListClient {
|
|||
public deleteExceptionListItem = jest.fn().mockResolvedValue(getExceptionListItemSchemaMock());
|
||||
public findExceptionListItem = jest.fn().mockResolvedValue(getFoundExceptionListItemSchemaMock());
|
||||
public findExceptionList = jest.fn().mockResolvedValue(getFoundExceptionListSchemaMock());
|
||||
public createTrustedAppsList = jest.fn().mockResolvedValue(getTrustedAppsListSchemaMock());
|
||||
}
|
||||
|
||||
export const getExceptionListClientMock = (): ExceptionListClient => {
|
||||
|
|
|
@ -11,3 +11,5 @@ export const policyIndexPattern = 'metrics-endpoint.policy-*';
|
|||
export const telemetryIndexPattern = 'metrics-endpoint.telemetry-*';
|
||||
export const LIMITED_CONCURRENCY_ENDPOINT_ROUTE_TAG = 'endpoint:limited-concurrency';
|
||||
export const LIMITED_CONCURRENCY_ENDPOINT_COUNT = 100;
|
||||
|
||||
export const TRUSTED_APPS_LIST_API = '/api/endpoint/trusted_apps';
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { GetTrustedAppsRequestSchema } from './trusted_apps';
|
||||
|
||||
describe('When invoking Trusted Apps Schema', () => {
|
||||
describe('for GET List', () => {
|
||||
const getListQueryParams = (page: unknown = 1, perPage: unknown = 20) => ({
|
||||
page,
|
||||
per_page: perPage,
|
||||
});
|
||||
const query = GetTrustedAppsRequestSchema.query;
|
||||
|
||||
describe('query param validation', () => {
|
||||
it('should return query params if valid', () => {
|
||||
expect(query.validate(getListQueryParams())).toEqual({
|
||||
page: 1,
|
||||
per_page: 20,
|
||||
});
|
||||
});
|
||||
|
||||
it('should use default values', () => {
|
||||
expect(query.validate(getListQueryParams(undefined, undefined))).toEqual({
|
||||
page: 1,
|
||||
per_page: 20,
|
||||
});
|
||||
expect(query.validate(getListQueryParams(undefined, 100))).toEqual({
|
||||
page: 1,
|
||||
per_page: 100,
|
||||
});
|
||||
expect(query.validate(getListQueryParams(10, undefined))).toEqual({
|
||||
page: 10,
|
||||
per_page: 20,
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw if `page` param is not a number', () => {
|
||||
expect(() => {
|
||||
query.validate(getListQueryParams('one'));
|
||||
}).toThrowError();
|
||||
});
|
||||
|
||||
it('should throw if `page` param is less than 1', () => {
|
||||
expect(() => {
|
||||
query.validate(getListQueryParams(0));
|
||||
}).toThrowError();
|
||||
expect(() => {
|
||||
query.validate(getListQueryParams(-1));
|
||||
}).toThrowError();
|
||||
});
|
||||
|
||||
it('should throw if `per_page` param is not a number', () => {
|
||||
expect(() => {
|
||||
query.validate(getListQueryParams(1, 'twenty'));
|
||||
}).toThrowError();
|
||||
});
|
||||
|
||||
it('should throw if `per_page` param is less than 1', () => {
|
||||
expect(() => {
|
||||
query.validate(getListQueryParams(1, 0));
|
||||
}).toThrowError();
|
||||
expect(() => {
|
||||
query.validate(getListQueryParams(1, -1));
|
||||
}).toThrowError();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
export const GetTrustedAppsRequestSchema = {
|
||||
query: schema.object({
|
||||
page: schema.maybe(schema.number({ defaultValue: 1, min: 1 })),
|
||||
per_page: schema.maybe(schema.number({ defaultValue: 20, min: 1 })),
|
||||
}),
|
||||
};
|
|
@ -5,8 +5,10 @@
|
|||
*/
|
||||
|
||||
import { ApplicationStart } from 'kibana/public';
|
||||
import { NewPackagePolicy, PackagePolicy } from '../../../ingest_manager/common';
|
||||
import { ManifestSchema } from './schema/manifest';
|
||||
import { NewPackagePolicy, PackagePolicy } from '../../../../ingest_manager/common';
|
||||
import { ManifestSchema } from '../schema/manifest';
|
||||
|
||||
export * from './trusted_apps';
|
||||
|
||||
/**
|
||||
* Supported React-Router state for the Policy Details page
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { TypeOf } from '@kbn/config-schema';
|
||||
import { GetTrustedAppsRequestSchema } from '../schema/trusted_apps';
|
||||
|
||||
/** API request params for retrieving a list of Trusted Apps */
|
||||
export type GetTrustedAppsListRequest = TypeOf<typeof GetTrustedAppsRequestSchema.query>;
|
||||
export interface GetTrustedListAppsResponse {
|
||||
per_page: number;
|
||||
page: number;
|
||||
total: number;
|
||||
data: TrustedApp[];
|
||||
}
|
||||
|
||||
interface MacosLinuxConditionEntry {
|
||||
field: 'hash' | 'path';
|
||||
type: 'match';
|
||||
operator: 'included';
|
||||
value: string;
|
||||
}
|
||||
|
||||
type WindowsConditionEntry =
|
||||
| MacosLinuxConditionEntry
|
||||
| (Omit<MacosLinuxConditionEntry, 'field'> & {
|
||||
field: 'signer';
|
||||
});
|
||||
|
||||
/** Type for a new Trusted App Entry */
|
||||
export type NewTrustedApp = {
|
||||
name: string;
|
||||
description?: string;
|
||||
} & (
|
||||
| {
|
||||
os: 'linux' | 'macos';
|
||||
entries: MacosLinuxConditionEntry[];
|
||||
}
|
||||
| {
|
||||
os: 'windows';
|
||||
entries: WindowsConditionEntry[];
|
||||
}
|
||||
);
|
||||
|
||||
/** A trusted app entry */
|
||||
export type TrustedApp = NewTrustedApp & {
|
||||
id: string;
|
||||
created_at: string;
|
||||
created_by: string;
|
||||
};
|
9
x-pack/plugins/security_solution/scripts/endpoint/load_trusted_apps.js
Executable file
9
x-pack/plugins/security_solution/scripts/endpoint/load_trusted_apps.js
Executable file
|
@ -0,0 +1,9 @@
|
|||
#!/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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
require('../../../../../src/setup_node_env');
|
||||
require('./trusted_apps').cli();
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
// @ts-ignore
|
||||
import minimist from 'minimist';
|
||||
import { KbnClient, ToolingLog } from '@kbn/dev-utils';
|
||||
import { ENDPOINT_TRUSTED_APPS_LIST_ID } from '../../../../lists/common/constants';
|
||||
import { TRUSTED_APPS_LIST_API } from '../../../common/endpoint/constants';
|
||||
import { ExceptionListItemSchema } from '../../../../lists/common/schemas/response';
|
||||
|
||||
interface RunOptions {
|
||||
count?: number;
|
||||
}
|
||||
|
||||
const logger = new ToolingLog({ level: 'info', writeTo: process.stdout });
|
||||
const separator = '----------------------------------------';
|
||||
|
||||
export const cli = async () => {
|
||||
const options: RunOptions = minimist(process.argv.slice(2), {
|
||||
default: {
|
||||
count: 10,
|
||||
},
|
||||
});
|
||||
logger.write(`${separator}
|
||||
Loading ${options.count} Trusted App Entries`);
|
||||
await run(options);
|
||||
logger.write(`Done!
|
||||
${separator}`);
|
||||
};
|
||||
|
||||
export const run: (options?: RunOptions) => Promise<ExceptionListItemSchema[]> = async ({
|
||||
count = 10,
|
||||
}: RunOptions = {}) => {
|
||||
const kbnClient = new KbnClient(logger, { url: 'http://elastic:changeme@localhost:5601' });
|
||||
|
||||
// touch the Trusted Apps List so it can be created
|
||||
await kbnClient.request({
|
||||
method: 'GET',
|
||||
path: TRUSTED_APPS_LIST_API,
|
||||
});
|
||||
|
||||
return Promise.all(
|
||||
Array.from({ length: count }, () => {
|
||||
return kbnClient
|
||||
.request({
|
||||
method: 'POST',
|
||||
path: '/api/exception_lists/items',
|
||||
body: generateTrustedAppEntry(),
|
||||
})
|
||||
.then<ExceptionListItemSchema>((item) => (item as unknown) as ExceptionListItemSchema);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
interface GenerateTrustedAppEntryOptions {
|
||||
os?: 'windows' | 'macos' | 'linux';
|
||||
name?: string;
|
||||
}
|
||||
|
||||
const generateTrustedAppEntry: (options?: GenerateTrustedAppEntryOptions) => object = ({
|
||||
os = 'windows',
|
||||
name = `Sample Endpoint Trusted App Entry ${Date.now()}`,
|
||||
} = {}) => {
|
||||
return {
|
||||
list_id: ENDPOINT_TRUSTED_APPS_LIST_ID,
|
||||
item_id: `generator_endpoint_trusted_apps_${generateUUID()}`,
|
||||
_tags: ['endpoint', `os:${os}`],
|
||||
tags: ['user added string for a tag', 'malware'],
|
||||
type: 'simple',
|
||||
description: 'This is a sample agnostic endpoint trusted app entry',
|
||||
name,
|
||||
namespace_type: 'agnostic',
|
||||
entries: [
|
||||
{
|
||||
field: 'actingProcess.file.signer',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: 'Elastic, N.V.',
|
||||
},
|
||||
{
|
||||
field: 'actingProcess.file.path',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: '/one/two/three',
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
|
@ -12,10 +12,12 @@ import {
|
|||
import { AgentService, IngestManagerStartContract } from '../../../ingest_manager/server';
|
||||
import { getPackagePolicyCreateCallback } from './ingest_integration';
|
||||
import { ManifestManager } from './services/artifacts';
|
||||
import { ExceptionListClient } from '../../../lists/server';
|
||||
|
||||
export type EndpointAppContextServiceStartContract = Partial<
|
||||
Pick<IngestManagerStartContract, 'agentService'>
|
||||
> & {
|
||||
exceptionsListService: ExceptionListClient;
|
||||
logger: Logger;
|
||||
manifestManager?: ManifestManager;
|
||||
registerIngestCallback?: IngestManagerStartContract['registerExternalCallback'];
|
||||
|
@ -30,9 +32,11 @@ export class EndpointAppContextService {
|
|||
private agentService: AgentService | undefined;
|
||||
private manifestManager: ManifestManager | undefined;
|
||||
private savedObjectsStart: SavedObjectsServiceStart | undefined;
|
||||
private exceptionsListService: ExceptionListClient | undefined;
|
||||
|
||||
public start(dependencies: EndpointAppContextServiceStartContract) {
|
||||
this.agentService = dependencies.agentService;
|
||||
this.exceptionsListService = dependencies.exceptionsListService;
|
||||
this.manifestManager = dependencies.manifestManager;
|
||||
this.savedObjectsStart = dependencies.savedObjectsStart;
|
||||
|
||||
|
@ -50,6 +54,13 @@ export class EndpointAppContextService {
|
|||
return this.agentService;
|
||||
}
|
||||
|
||||
public getExceptionsList() {
|
||||
if (!this.exceptionsListService) {
|
||||
throw new Error('exceptionsListService not set');
|
||||
}
|
||||
return this.exceptionsListService;
|
||||
}
|
||||
|
||||
public getManifestManager(): ManifestManager | undefined {
|
||||
return this.manifestManager;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
import { ManifestManager } from './services/artifacts/manifest_manager/manifest_manager';
|
||||
import { getManifestManagerMock } from './services/artifacts/manifest_manager/manifest_manager.mock';
|
||||
import { EndpointAppContext } from './types';
|
||||
import { listMock } from '../../../lists/server/mocks';
|
||||
|
||||
/**
|
||||
* Creates a mocked EndpointAppContext.
|
||||
|
@ -58,6 +59,7 @@ export const createMockEndpointAppContextServiceStartContract = (): jest.Mocked<
|
|||
> => {
|
||||
return {
|
||||
agentService: createMockAgentService(),
|
||||
exceptionsListService: listMock.getExceptionListClient(),
|
||||
logger: loggingSystemMock.create().get('mock_endpoint_app_context'),
|
||||
savedObjectsStart: savedObjectsServiceMock.createStartContract(),
|
||||
manifestManager: getManifestManagerMock(),
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { RequestHandler } from 'kibana/server';
|
||||
import {
|
||||
GetTrustedAppsListRequest,
|
||||
GetTrustedListAppsResponse,
|
||||
} from '../../../../common/endpoint/types';
|
||||
import { EndpointAppContext } from '../../types';
|
||||
import { exceptionItemToTrustedAppItem } from './utils';
|
||||
import { ENDPOINT_TRUSTED_APPS_LIST_ID } from '../../../../../lists/common/constants';
|
||||
|
||||
export const getTrustedAppsListRouteHandler = (
|
||||
endpointAppContext: EndpointAppContext
|
||||
): RequestHandler<undefined, GetTrustedAppsListRequest> => {
|
||||
const logger = endpointAppContext.logFactory.get('trusted_apps');
|
||||
|
||||
return async (context, req, res) => {
|
||||
const exceptionsListService = endpointAppContext.service.getExceptionsList();
|
||||
const { page, per_page: perPage } = req.query;
|
||||
|
||||
try {
|
||||
// Ensure list is created if it does not exist
|
||||
await exceptionsListService?.createTrustedAppsList();
|
||||
const results = await exceptionsListService.findExceptionListItem({
|
||||
listId: ENDPOINT_TRUSTED_APPS_LIST_ID,
|
||||
page,
|
||||
perPage,
|
||||
filter: undefined,
|
||||
namespaceType: 'agnostic',
|
||||
sortField: 'name',
|
||||
sortOrder: 'asc',
|
||||
});
|
||||
const body: GetTrustedListAppsResponse = {
|
||||
data: results?.data.map(exceptionItemToTrustedAppItem) ?? [],
|
||||
total: results?.total ?? 0,
|
||||
page: results?.page ?? 1,
|
||||
per_page: results?.per_page ?? perPage!,
|
||||
};
|
||||
return res.ok({ body });
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return res.internalError({ body: error });
|
||||
}
|
||||
};
|
||||
};
|
|
@ -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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { IRouter } from 'kibana/server';
|
||||
import { GetTrustedAppsRequestSchema } from '../../../../common/endpoint/schema/trusted_apps';
|
||||
import { TRUSTED_APPS_LIST_API } from '../../../../common/endpoint/constants';
|
||||
import { getTrustedAppsListRouteHandler } from './handlers';
|
||||
import { EndpointAppContext } from '../../types';
|
||||
|
||||
export const registerTrustedAppsRoutes = (
|
||||
router: IRouter,
|
||||
endpointAppContext: EndpointAppContext
|
||||
) => {
|
||||
// GET list
|
||||
router.get(
|
||||
{
|
||||
path: TRUSTED_APPS_LIST_API,
|
||||
validate: GetTrustedAppsRequestSchema,
|
||||
options: { authRequired: true },
|
||||
},
|
||||
getTrustedAppsListRouteHandler(endpointAppContext)
|
||||
);
|
||||
};
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EndpointAppContextService } from '../../endpoint_app_context_services';
|
||||
import {
|
||||
createMockEndpointAppContext,
|
||||
createMockEndpointAppContextServiceStartContract,
|
||||
} from '../../mocks';
|
||||
import { IRouter, RequestHandler } from 'kibana/server';
|
||||
import { httpServerMock, httpServiceMock } from '../../../../../../../src/core/server/mocks';
|
||||
import { registerTrustedAppsRoutes } from './index';
|
||||
import { TRUSTED_APPS_LIST_API } from '../../../../common/endpoint/constants';
|
||||
import { GetTrustedAppsListRequest } from '../../../../common/endpoint/types';
|
||||
import { xpackMocks } from '../../../../../../mocks';
|
||||
import { ENDPOINT_TRUSTED_APPS_LIST_ID } from '../../../../../lists/common/constants';
|
||||
import { EndpointAppContext } from '../../types';
|
||||
import { ExceptionListClient } from '../../../../../lists/server';
|
||||
|
||||
describe('when invoking endpoint trusted apps route handlers', () => {
|
||||
let routerMock: jest.Mocked<IRouter>;
|
||||
let endpointAppContextService: EndpointAppContextService;
|
||||
let context: ReturnType<typeof xpackMocks.createRequestHandlerContext>;
|
||||
let response: ReturnType<typeof httpServerMock.createResponseFactory>;
|
||||
let exceptionsListClient: jest.Mocked<ExceptionListClient>;
|
||||
let endpointAppContext: EndpointAppContext;
|
||||
|
||||
beforeEach(() => {
|
||||
routerMock = httpServiceMock.createRouter();
|
||||
endpointAppContextService = new EndpointAppContextService();
|
||||
const startContract = createMockEndpointAppContextServiceStartContract();
|
||||
exceptionsListClient = startContract.exceptionsListService as jest.Mocked<ExceptionListClient>;
|
||||
endpointAppContextService.start(startContract);
|
||||
endpointAppContext = {
|
||||
...createMockEndpointAppContext(),
|
||||
service: endpointAppContextService,
|
||||
};
|
||||
registerTrustedAppsRoutes(routerMock, endpointAppContext);
|
||||
|
||||
// For use in individual API calls
|
||||
context = xpackMocks.createRequestHandlerContext();
|
||||
response = httpServerMock.createResponseFactory();
|
||||
});
|
||||
|
||||
describe('when fetching list of trusted apps', () => {
|
||||
let routeHandler: RequestHandler<undefined, GetTrustedAppsListRequest>;
|
||||
const createListRequest = (page: number = 1, perPage: number = 20) => {
|
||||
return httpServerMock.createKibanaRequest<undefined, GetTrustedAppsListRequest>({
|
||||
path: TRUSTED_APPS_LIST_API,
|
||||
method: 'get',
|
||||
query: {
|
||||
page,
|
||||
per_page: perPage,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
// Get the registered List handler from the IRouter instance
|
||||
[, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) =>
|
||||
path.startsWith(TRUSTED_APPS_LIST_API)
|
||||
)!;
|
||||
});
|
||||
|
||||
it('should create the Trusted Apps List first', async () => {
|
||||
const request = createListRequest();
|
||||
await routeHandler(context, request, response);
|
||||
expect(exceptionsListClient.createTrustedAppsList).toHaveBeenCalled();
|
||||
expect(response.ok).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should pass pagination query params to exception list service', async () => {
|
||||
const request = createListRequest(10, 100);
|
||||
const emptyResponse = {
|
||||
data: [],
|
||||
page: 10,
|
||||
per_page: 100,
|
||||
total: 0,
|
||||
};
|
||||
|
||||
exceptionsListClient.findExceptionListItem.mockResolvedValue(emptyResponse);
|
||||
await routeHandler(context, request, response);
|
||||
|
||||
expect(response.ok).toHaveBeenCalledWith({ body: emptyResponse });
|
||||
expect(exceptionsListClient.findExceptionListItem).toHaveBeenCalledWith({
|
||||
listId: ENDPOINT_TRUSTED_APPS_LIST_ID,
|
||||
page: 10,
|
||||
perPage: 100,
|
||||
filter: undefined,
|
||||
namespaceType: 'agnostic',
|
||||
sortField: 'name',
|
||||
sortOrder: 'asc',
|
||||
});
|
||||
});
|
||||
|
||||
it('should log unexpected error if one occurs', async () => {
|
||||
exceptionsListClient.findExceptionListItem.mockImplementation(() => {
|
||||
throw new Error('expected error');
|
||||
});
|
||||
const request = createListRequest(10, 100);
|
||||
await routeHandler(context, request, response);
|
||||
expect(response.internalError).toHaveBeenCalled();
|
||||
expect(endpointAppContext.logFactory.get('trusted_apps').error).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { ExceptionListItemSchema } from '../../../../../lists/common/shared_exports';
|
||||
import { TrustedApp } from '../../../../common/endpoint/types';
|
||||
|
||||
/**
|
||||
* Map an ExcptionListItem to a TrustedApp item
|
||||
* @param exceptionListItem
|
||||
*/
|
||||
export const exceptionItemToTrustedAppItem = (
|
||||
exceptionListItem: ExceptionListItemSchema
|
||||
): TrustedApp => {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const { entries, description, created_by, created_at, name, _tags, id } = exceptionListItem;
|
||||
const os = osFromTagsList(_tags);
|
||||
return {
|
||||
entries,
|
||||
description,
|
||||
created_at,
|
||||
created_by,
|
||||
name,
|
||||
os,
|
||||
id,
|
||||
} as TrustedApp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the OS entry from a list of tags (property returned with ExcptionListItem).
|
||||
* For Trusted Apps each entry must have at MOST 1 OS.
|
||||
* */
|
||||
const osFromTagsList = (tags: string[]): TrustedApp['os'] | 'unknown' => {
|
||||
for (const tag of tags) {
|
||||
if (tag.startsWith('os:')) {
|
||||
return tag.substr(3) as TrustedApp['os'];
|
||||
}
|
||||
}
|
||||
return 'unknown';
|
||||
};
|
|
@ -59,6 +59,7 @@ import { EndpointAppContext } from './endpoint/types';
|
|||
import { registerDownloadExceptionListRoute } from './endpoint/routes/artifacts';
|
||||
import { initUsageCollectors } from './usage';
|
||||
import { AppRequestContext } from './types';
|
||||
import { registerTrustedAppsRoutes } from './endpoint/routes/trusted_apps';
|
||||
import { securitySolutionSearchStrategyProvider } from './search_strategy/security_solution';
|
||||
|
||||
export interface SetupPlugins {
|
||||
|
@ -166,6 +167,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
registerLimitedConcurrencyRoutes(core);
|
||||
registerResolverRoutes(router, endpointContext);
|
||||
registerPolicyRoutes(router, endpointContext);
|
||||
registerTrustedAppsRoutes(router, endpointContext);
|
||||
registerDownloadExceptionListRoute(router, endpointContext, this.exceptionsCache);
|
||||
|
||||
plugins.features.registerFeature({
|
||||
|
@ -305,6 +307,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
|
||||
this.endpointAppContextService.start({
|
||||
agentService: plugins.ingestManager?.agentService,
|
||||
exceptionsListService: this.lists!.getExceptionListClient(savedObjectsClient, 'kibana'),
|
||||
logger: this.logger,
|
||||
manifestManager,
|
||||
registerIngestCallback,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue