[Security solution] [Endpoint] Adds API check for existing policies when saving/creating trusted apps (#106110) (#106975)
* Adds API check for existing policies when saving/creating trusted apps * Fixes wrong error message * Fixes tests and replaces policy id by policy name in error message * Updates error message text * Address pr comments and remove old comments in code * Addressed some pr comments and added type to the error response in order to know which kind of response it is * removed unused imports Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: David Sánchez <davidsansol92@gmail.com>
This commit is contained in:
parent
9c28da7f73
commit
3e2a915908
8 changed files with 377 additions and 52 deletions
|
@ -5,10 +5,12 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ResponseErrorAttributes } from 'kibana/server';
|
||||
export interface ServerApiError {
|
||||
statusCode: number;
|
||||
error: string;
|
||||
message: string;
|
||||
attributes?: ResponseErrorAttributes | undefined;
|
||||
}
|
||||
|
||||
export interface SecuritySolutionUiConfigType {
|
||||
|
|
|
@ -66,14 +66,6 @@ export const CreateTrustedAppFlyout = memo<CreateTrustedAppFlyoutProps>(
|
|||
|
||||
const dataTestSubj = flyoutProps['data-test-subj'];
|
||||
|
||||
const creationErrorsMessage = useMemo<string | undefined>(
|
||||
() =>
|
||||
creationErrors
|
||||
? CREATE_TRUSTED_APP_ERROR[creationErrors.message.replace(/(\[(.*)\]\: )/, '')] ||
|
||||
creationErrors.message
|
||||
: undefined,
|
||||
[creationErrors]
|
||||
);
|
||||
const policies = useMemo<CreateTrustedAppFormProps['policies']>(() => {
|
||||
return {
|
||||
// Casting is needed due to the use of `Immutable<>` on the return value from the selector above
|
||||
|
@ -82,6 +74,24 @@ export const CreateTrustedAppFlyout = memo<CreateTrustedAppFlyoutProps>(
|
|||
};
|
||||
}, [isLoadingPolicies, policyList]);
|
||||
|
||||
const creationErrorsMessage = useMemo<string | undefined>(() => {
|
||||
let errorMessage = creationErrors
|
||||
? CREATE_TRUSTED_APP_ERROR[creationErrors.message.replace(/(\[(.*)\]\: )/, '')] ||
|
||||
creationErrors.message
|
||||
: undefined;
|
||||
|
||||
if (
|
||||
creationErrors &&
|
||||
creationErrors.attributes &&
|
||||
creationErrors.attributes.type === 'TrustedApps/PolicyNotFound'
|
||||
) {
|
||||
policies.options.forEach((policy) => {
|
||||
errorMessage = errorMessage?.replace(policy.id, policy.name);
|
||||
});
|
||||
}
|
||||
return errorMessage;
|
||||
}, [creationErrors, policies]);
|
||||
|
||||
const getTestId = useTestIdGenerator(dataTestSubj);
|
||||
|
||||
const handleCancelClick = useCallback(() => {
|
||||
|
|
|
@ -12,6 +12,17 @@ export class TrustedAppNotFoundError extends Error {
|
|||
super(`Trusted Application (${id}) not found`);
|
||||
}
|
||||
}
|
||||
export class TrustedAppPolicyNotExistsError extends Error {
|
||||
public readonly type = 'TrustedApps/PolicyNotFound';
|
||||
|
||||
constructor(name: string, policyIds: string[]) {
|
||||
super(
|
||||
`Trusted Application (${name}) is assigned with a policy that no longer exists: ${policyIds.join(
|
||||
', '
|
||||
)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class TrustedAppVersionConflictError extends Error {
|
||||
constructor(id: string, public sourceError: Error) {
|
||||
|
|
|
@ -5,10 +5,14 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { KibanaResponseFactory } from 'kibana/server';
|
||||
import { KibanaResponseFactory, SavedObjectsClientContract } from 'kibana/server';
|
||||
|
||||
import { xpackMocks } from '../../../fixtures';
|
||||
import { loggingSystemMock, httpServerMock } from '../../../../../../../src/core/server/mocks';
|
||||
import {
|
||||
loggingSystemMock,
|
||||
httpServerMock,
|
||||
savedObjectsClientMock,
|
||||
} from '../../../../../../../src/core/server/mocks';
|
||||
import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { listMock } from '../../../../../lists/server/mocks';
|
||||
import { ExceptionListClient } from '../../../../../lists/server';
|
||||
|
@ -32,9 +36,16 @@ import {
|
|||
getTrustedAppsUpdateRouteHandler,
|
||||
} from './handlers';
|
||||
import type { SecuritySolutionRequestHandlerContext } from '../../../types';
|
||||
import { TrustedAppNotFoundError, TrustedAppVersionConflictError } from './errors';
|
||||
import {
|
||||
TrustedAppNotFoundError,
|
||||
TrustedAppVersionConflictError,
|
||||
TrustedAppPolicyNotExistsError,
|
||||
} from './errors';
|
||||
import { updateExceptionListItemImplementationMock } from './test_utils';
|
||||
import { Logger } from '@kbn/logging';
|
||||
import { PackagePolicyServiceInterface } from '../../../../../fleet/server';
|
||||
import { createPackagePolicyServiceMock } from '../../../../../fleet/server/mocks';
|
||||
import { getPackagePoliciesResponse, getTrustedAppByPolicy } from './mocks';
|
||||
|
||||
const EXCEPTION_LIST_ITEM: ExceptionListItemSchema = {
|
||||
_version: 'abc123',
|
||||
|
@ -88,7 +99,14 @@ const TRUSTED_APP: TrustedApp = {
|
|||
],
|
||||
};
|
||||
|
||||
const packagePolicyClient = createPackagePolicyServiceMock() as jest.Mocked<PackagePolicyServiceInterface>;
|
||||
const savedObjectClient = savedObjectsClientMock.create() as jest.Mocked<SavedObjectsClientContract>;
|
||||
|
||||
describe('handlers', () => {
|
||||
beforeEach(() => {
|
||||
packagePolicyClient.getByIDs.mockReset();
|
||||
});
|
||||
|
||||
const createAppContextMock = () => {
|
||||
const context = {
|
||||
logFactory: loggingSystemMock.create(),
|
||||
|
@ -97,6 +115,9 @@ describe('handlers', () => {
|
|||
experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
|
||||
};
|
||||
|
||||
context.service.getPackagePolicyService = () => packagePolicyClient;
|
||||
context.service.getScopedSavedObjectsClient = () => savedObjectClient;
|
||||
|
||||
// Ensure that `logFactory.get()` always returns the same instance for the same given prefix
|
||||
const instances = new Map<string, ReturnType<typeof context.logFactory.get>>();
|
||||
const logFactoryGetMock = context.logFactory.get.getMockImplementation();
|
||||
|
@ -224,6 +245,28 @@ describe('handlers', () => {
|
|||
)
|
||||
).rejects.toThrowError(error);
|
||||
});
|
||||
|
||||
it("should return error when policy doesn't exists", async () => {
|
||||
const mockResponse = httpServerMock.createResponseFactory();
|
||||
packagePolicyClient.getByIDs.mockReset();
|
||||
packagePolicyClient.getByIDs.mockResolvedValueOnce(getPackagePoliciesResponse());
|
||||
|
||||
const trustedAppByPolicy = getTrustedAppByPolicy();
|
||||
await createTrustedAppHandler(
|
||||
createHandlerContextMock(),
|
||||
httpServerMock.createKibanaRequest({ body: trustedAppByPolicy }),
|
||||
mockResponse
|
||||
);
|
||||
|
||||
const error = new TrustedAppPolicyNotExistsError(trustedAppByPolicy.name, [
|
||||
'9da95be9-9bee-4761-a8c4-28d6d9bd8c71',
|
||||
]);
|
||||
|
||||
expect(appContextMock.logFactory.get('trusted_apps').error).toHaveBeenCalledWith(error);
|
||||
expect(mockResponse.badRequest).toHaveBeenCalledWith({
|
||||
body: { message: error.message, attributes: { type: error.type } },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTrustedAppsListRouteHandler', () => {
|
||||
|
@ -508,5 +551,23 @@ describe('handlers', () => {
|
|||
body: expect.any(TrustedAppVersionConflictError),
|
||||
});
|
||||
});
|
||||
|
||||
it("should return error when policy doesn't exists", async () => {
|
||||
packagePolicyClient.getByIDs.mockReset();
|
||||
packagePolicyClient.getByIDs.mockResolvedValueOnce(getPackagePoliciesResponse());
|
||||
|
||||
const trustedAppByPolicy = getTrustedAppByPolicy();
|
||||
await updateHandler(
|
||||
createHandlerContextMock(),
|
||||
httpServerMock.createKibanaRequest({ body: trustedAppByPolicy }),
|
||||
mockResponse
|
||||
);
|
||||
|
||||
expect(appContextMock.logFactory.get('trusted_apps').error).toHaveBeenCalledWith(
|
||||
new TrustedAppPolicyNotExistsError(trustedAppByPolicy.name, [
|
||||
'9da95be9-9bee-4761-a8c4-28d6d9bd8c71',
|
||||
])
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -28,7 +28,12 @@ import {
|
|||
getTrustedAppsSummary,
|
||||
updateTrustedApp,
|
||||
} from './service';
|
||||
import { TrustedAppNotFoundError, TrustedAppVersionConflictError } from './errors';
|
||||
import {
|
||||
TrustedAppNotFoundError,
|
||||
TrustedAppVersionConflictError,
|
||||
TrustedAppPolicyNotExistsError,
|
||||
} from './errors';
|
||||
import { PackagePolicyServiceInterface } from '../../../../../fleet/server';
|
||||
|
||||
const getBodyAfterFeatureFlagCheck = (
|
||||
body: PutTrustedAppUpdateRequest | PostTrustedAppCreateRequest,
|
||||
|
@ -54,6 +59,18 @@ const exceptionListClientFromContext = (
|
|||
return exceptionLists;
|
||||
};
|
||||
|
||||
const packagePolicyClientFromEndpointContext = (
|
||||
endpointAppContext: EndpointAppContext
|
||||
): PackagePolicyServiceInterface => {
|
||||
const packagePolicy = endpointAppContext.service.getPackagePolicyService();
|
||||
|
||||
if (!packagePolicy) {
|
||||
throw new Error('Package policy service not found');
|
||||
}
|
||||
|
||||
return packagePolicy;
|
||||
};
|
||||
|
||||
const errorHandler = <E extends Error>(
|
||||
logger: Logger,
|
||||
res: KibanaResponseFactory,
|
||||
|
@ -64,6 +81,11 @@ const errorHandler = <E extends Error>(
|
|||
return res.notFound({ body: error });
|
||||
}
|
||||
|
||||
if (error instanceof TrustedAppPolicyNotExistsError) {
|
||||
logger.error(error);
|
||||
return res.badRequest({ body: { message: error.message, attributes: { type: error.type } } });
|
||||
}
|
||||
|
||||
if (error instanceof TrustedAppVersionConflictError) {
|
||||
logger.error(error);
|
||||
return res.conflict({ body: error });
|
||||
|
@ -150,7 +172,12 @@ export const getTrustedAppsCreateRouteHandler = (
|
|||
const body = getBodyAfterFeatureFlagCheck(req.body, endpointAppContext);
|
||||
|
||||
return res.ok({
|
||||
body: await createTrustedApp(exceptionListClientFromContext(context), body),
|
||||
body: await createTrustedApp(
|
||||
exceptionListClientFromContext(context),
|
||||
context.core.savedObjects.client,
|
||||
packagePolicyClientFromEndpointContext(endpointAppContext),
|
||||
body
|
||||
),
|
||||
});
|
||||
} catch (error) {
|
||||
return errorHandler(logger, res, error);
|
||||
|
@ -173,7 +200,13 @@ export const getTrustedAppsUpdateRouteHandler = (
|
|||
const body = getBodyAfterFeatureFlagCheck(req.body, endpointAppContext);
|
||||
|
||||
return res.ok({
|
||||
body: await updateTrustedApp(exceptionListClientFromContext(context), req.params.id, body),
|
||||
body: await updateTrustedApp(
|
||||
exceptionListClientFromContext(context),
|
||||
context.core.savedObjects.client,
|
||||
packagePolicyClientFromEndpointContext(endpointAppContext),
|
||||
req.params.id,
|
||||
body
|
||||
),
|
||||
});
|
||||
} catch (error) {
|
||||
return errorHandler(logger, res, error);
|
||||
|
|
|
@ -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 { PackagePolicy } from '../../../../../fleet/common';
|
||||
|
||||
import {
|
||||
ConditionEntryField,
|
||||
OperatingSystem,
|
||||
TrustedApp,
|
||||
} from '../../../../common/endpoint/types';
|
||||
import { createConditionEntry } from './mapping';
|
||||
|
||||
export const getTrustedAppByPolicy = function (): TrustedApp {
|
||||
return {
|
||||
id: '123',
|
||||
version: 'abc123',
|
||||
created_at: '11/11/2011T11:11:11.111',
|
||||
created_by: 'admin',
|
||||
updated_at: '11/11/2011T11:11:11.111',
|
||||
updated_by: 'admin',
|
||||
name: 'linux trusted app 1',
|
||||
description: 'Linux trusted app 1',
|
||||
os: OperatingSystem.LINUX,
|
||||
effectScope: {
|
||||
type: 'policy',
|
||||
policies: ['e5cbb9cf-98aa-4303-a04b-6a1165915079', '9da95be9-9bee-4761-a8c4-28d6d9bd8c71'],
|
||||
},
|
||||
entries: [
|
||||
createConditionEntry(ConditionEntryField.HASH, 'match', '1234234659af249ddf3e40864e9fb241'),
|
||||
createConditionEntry(ConditionEntryField.PATH, 'match', '/bin/malware'),
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
export const getPackagePoliciesResponse = function (): PackagePolicy[] {
|
||||
return [
|
||||
// Next line is ts-ignored as this is the response when the policy doesn't exists but the type is complaining about it.
|
||||
// @ts-ignore
|
||||
{ id: '9da95be9-9bee-4761-a8c4-28d6d9bd8c71', version: undefined },
|
||||
{
|
||||
id: 'e5cbb9cf-98aa-4303-a04b-6a1165915079',
|
||||
version: 'Wzc0NDk5LDFd',
|
||||
name: 'EI 2',
|
||||
description: '',
|
||||
namespace: 'default',
|
||||
policy_id: '9fd2ac50-e86f-11eb-a87f-51e16104076a',
|
||||
enabled: true,
|
||||
output_id: '',
|
||||
inputs: [],
|
||||
package: { name: 'endpoint', title: 'Endpoint Security', version: '0.20.1' },
|
||||
revision: 3,
|
||||
created_at: '2021-07-19T09:00:45.608Z',
|
||||
created_by: 'elastic',
|
||||
updated_at: '2021-07-19T09:02:47.193Z',
|
||||
updated_by: 'system',
|
||||
},
|
||||
];
|
||||
};
|
|
@ -7,9 +7,14 @@
|
|||
|
||||
import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { listMock } from '../../../../../lists/server/mocks';
|
||||
import { createPackagePolicyServiceMock } from '../../../../../fleet/server/mocks';
|
||||
import { savedObjectsClientMock } from '../../../../../../../src/core/server/mocks';
|
||||
import { PackagePolicyServiceInterface } from '../../../../../fleet/server';
|
||||
import type { SavedObjectsClientContract } from 'kibana/server';
|
||||
import { ExceptionListClient } from '../../../../../lists/server';
|
||||
import {
|
||||
ConditionEntryField,
|
||||
MaybeImmutable,
|
||||
OperatingSystem,
|
||||
TrustedApp,
|
||||
} from '../../../../common/endpoint/types';
|
||||
|
@ -22,12 +27,19 @@ import {
|
|||
getTrustedAppsSummary,
|
||||
updateTrustedApp,
|
||||
} from './service';
|
||||
import { TrustedAppNotFoundError, TrustedAppVersionConflictError } from './errors';
|
||||
import {
|
||||
TrustedAppNotFoundError,
|
||||
TrustedAppVersionConflictError,
|
||||
TrustedAppPolicyNotExistsError,
|
||||
} from './errors';
|
||||
import { toUpdateTrustedApp } from '../../../../common/endpoint/service/trusted_apps/to_update_trusted_app';
|
||||
import { updateExceptionListItemImplementationMock } from './test_utils';
|
||||
import { ENDPOINT_TRUSTED_APPS_LIST_ID } from '@kbn/securitysolution-list-constants';
|
||||
import { getPackagePoliciesResponse, getTrustedAppByPolicy } from './mocks';
|
||||
|
||||
const exceptionsListClient = listMock.getExceptionListClient() as jest.Mocked<ExceptionListClient>;
|
||||
const packagePolicyClient = createPackagePolicyServiceMock() as jest.Mocked<PackagePolicyServiceInterface>;
|
||||
const savedObjectClient = savedObjectsClientMock.create() as jest.Mocked<SavedObjectsClientContract>;
|
||||
|
||||
const EXCEPTION_LIST_ITEM: ExceptionListItemSchema = {
|
||||
_version: 'abc123',
|
||||
|
@ -76,6 +88,7 @@ describe('service', () => {
|
|||
exceptionsListClient.createExceptionListItem.mockReset();
|
||||
exceptionsListClient.findExceptionListItem.mockReset();
|
||||
exceptionsListClient.createTrustedAppsList.mockReset();
|
||||
packagePolicyClient.getByIDs.mockReset();
|
||||
});
|
||||
|
||||
describe('deleteTrustedApp', () => {
|
||||
|
@ -103,20 +116,25 @@ describe('service', () => {
|
|||
it('should create trusted app', async () => {
|
||||
exceptionsListClient.createExceptionListItem.mockResolvedValue(EXCEPTION_LIST_ITEM);
|
||||
|
||||
const result = await createTrustedApp(exceptionsListClient, {
|
||||
name: 'linux trusted app 1',
|
||||
description: 'Linux trusted app 1',
|
||||
effectScope: { type: 'global' },
|
||||
os: OperatingSystem.LINUX,
|
||||
entries: [
|
||||
createConditionEntry(ConditionEntryField.PATH, 'match', '/bin/malware'),
|
||||
createConditionEntry(
|
||||
ConditionEntryField.HASH,
|
||||
'match',
|
||||
'1234234659af249ddf3e40864e9fb241'
|
||||
),
|
||||
],
|
||||
});
|
||||
const result = await createTrustedApp(
|
||||
exceptionsListClient,
|
||||
savedObjectClient,
|
||||
packagePolicyClient,
|
||||
{
|
||||
name: 'linux trusted app 1',
|
||||
description: 'Linux trusted app 1',
|
||||
effectScope: { type: 'global' },
|
||||
os: OperatingSystem.LINUX,
|
||||
entries: [
|
||||
createConditionEntry(ConditionEntryField.PATH, 'match', '/bin/malware'),
|
||||
createConditionEntry(
|
||||
ConditionEntryField.HASH,
|
||||
'match',
|
||||
'1234234659af249ddf3e40864e9fb241'
|
||||
),
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
expect(result).toEqual({ data: TRUSTED_APP });
|
||||
|
||||
|
@ -126,25 +144,57 @@ describe('service', () => {
|
|||
it('should create trusted app with correct wildcard type', async () => {
|
||||
exceptionsListClient.createExceptionListItem.mockResolvedValue(EXCEPTION_LIST_ITEM);
|
||||
|
||||
const result = await createTrustedApp(exceptionsListClient, {
|
||||
name: 'linux trusted app 1',
|
||||
description: 'Linux trusted app 1',
|
||||
effectScope: { type: 'global' },
|
||||
os: OperatingSystem.LINUX,
|
||||
entries: [
|
||||
createConditionEntry(ConditionEntryField.PATH, 'wildcard', '/bin/malware'),
|
||||
createConditionEntry(
|
||||
ConditionEntryField.HASH,
|
||||
'wildcard',
|
||||
'1234234659af249ddf3e40864e9fb241'
|
||||
),
|
||||
],
|
||||
});
|
||||
const result = await createTrustedApp(
|
||||
exceptionsListClient,
|
||||
savedObjectClient,
|
||||
packagePolicyClient,
|
||||
{
|
||||
name: 'linux trusted app 1',
|
||||
description: 'Linux trusted app 1',
|
||||
effectScope: { type: 'global' },
|
||||
os: OperatingSystem.LINUX,
|
||||
entries: [
|
||||
createConditionEntry(ConditionEntryField.PATH, 'wildcard', '/bin/malware'),
|
||||
createConditionEntry(
|
||||
ConditionEntryField.HASH,
|
||||
'wildcard',
|
||||
'1234234659af249ddf3e40864e9fb241'
|
||||
),
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
expect(result).toEqual({ data: TRUSTED_APP });
|
||||
|
||||
expect(exceptionsListClient.createTrustedAppsList).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should throw wrong policy error if some policy doesn't exists", async () => {
|
||||
packagePolicyClient.getByIDs.mockReset();
|
||||
packagePolicyClient.getByIDs.mockResolvedValueOnce(getPackagePoliciesResponse());
|
||||
await expect(
|
||||
createTrustedApp(exceptionsListClient, savedObjectClient, packagePolicyClient, {
|
||||
name: 'linux trusted app 1',
|
||||
description: 'Linux trusted app 1',
|
||||
effectScope: {
|
||||
type: 'policy',
|
||||
policies: [
|
||||
'e5cbb9cf-98aa-4303-a04b-6a1165915079',
|
||||
'9da95be9-9bee-4761-a8c4-28d6d9bd8c71',
|
||||
],
|
||||
},
|
||||
os: OperatingSystem.LINUX,
|
||||
entries: [
|
||||
createConditionEntry(ConditionEntryField.PATH, 'wildcard', '/bin/malware'),
|
||||
createConditionEntry(
|
||||
ConditionEntryField.HASH,
|
||||
'wildcard',
|
||||
'1234234659af249ddf3e40864e9fb241'
|
||||
),
|
||||
],
|
||||
})
|
||||
).rejects.toBeInstanceOf(TrustedAppPolicyNotExistsError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTrustedAppsList', () => {
|
||||
|
@ -251,7 +301,13 @@ describe('service', () => {
|
|||
trustedAppForUpdate.entries = [trustedAppForUpdate.entries[0]];
|
||||
|
||||
await expect(
|
||||
updateTrustedApp(exceptionsListClient, TRUSTED_APP.id, trustedAppForUpdate)
|
||||
updateTrustedApp(
|
||||
exceptionsListClient,
|
||||
savedObjectClient,
|
||||
packagePolicyClient,
|
||||
TRUSTED_APP.id,
|
||||
trustedAppForUpdate
|
||||
)
|
||||
).resolves.toEqual({
|
||||
data: {
|
||||
created_at: '11/11/2011T11:11:11.111',
|
||||
|
@ -281,7 +337,13 @@ describe('service', () => {
|
|||
it('should throw a Not Found error if trusted app is not found prior to making update', async () => {
|
||||
exceptionsListClient.getExceptionListItem.mockResolvedValueOnce(null);
|
||||
await expect(
|
||||
updateTrustedApp(exceptionsListClient, TRUSTED_APP.id, toUpdateTrustedApp(TRUSTED_APP))
|
||||
updateTrustedApp(
|
||||
exceptionsListClient,
|
||||
savedObjectClient,
|
||||
packagePolicyClient,
|
||||
TRUSTED_APP.id,
|
||||
toUpdateTrustedApp(TRUSTED_APP)
|
||||
)
|
||||
).rejects.toBeInstanceOf(TrustedAppNotFoundError);
|
||||
});
|
||||
|
||||
|
@ -292,7 +354,13 @@ describe('service', () => {
|
|||
);
|
||||
|
||||
await expect(
|
||||
updateTrustedApp(exceptionsListClient, TRUSTED_APP.id, toUpdateTrustedApp(TRUSTED_APP))
|
||||
updateTrustedApp(
|
||||
exceptionsListClient,
|
||||
savedObjectClient,
|
||||
packagePolicyClient,
|
||||
TRUSTED_APP.id,
|
||||
toUpdateTrustedApp(TRUSTED_APP)
|
||||
)
|
||||
).rejects.toBeInstanceOf(TrustedAppVersionConflictError);
|
||||
});
|
||||
|
||||
|
@ -305,9 +373,30 @@ describe('service', () => {
|
|||
exceptionsListClient.getExceptionListItem.mockResolvedValueOnce(null);
|
||||
|
||||
await expect(
|
||||
updateTrustedApp(exceptionsListClient, TRUSTED_APP.id, toUpdateTrustedApp(TRUSTED_APP))
|
||||
updateTrustedApp(
|
||||
exceptionsListClient,
|
||||
savedObjectClient,
|
||||
packagePolicyClient,
|
||||
TRUSTED_APP.id,
|
||||
toUpdateTrustedApp(TRUSTED_APP)
|
||||
)
|
||||
).rejects.toBeInstanceOf(TrustedAppNotFoundError);
|
||||
});
|
||||
|
||||
it("should throw wrong policy error if some policy doesn't exists", async () => {
|
||||
packagePolicyClient.getByIDs.mockReset();
|
||||
packagePolicyClient.getByIDs.mockResolvedValueOnce(getPackagePoliciesResponse());
|
||||
const trustedAppByPolicy = getTrustedAppByPolicy();
|
||||
await expect(
|
||||
updateTrustedApp(
|
||||
exceptionsListClient,
|
||||
savedObjectClient,
|
||||
packagePolicyClient,
|
||||
trustedAppByPolicy.id,
|
||||
toUpdateTrustedApp(trustedAppByPolicy as MaybeImmutable<TrustedApp>)
|
||||
)
|
||||
).rejects.toBeInstanceOf(TrustedAppPolicyNotExistsError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTrustedApp', () => {
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { SavedObjectsClientContract } from 'kibana/server';
|
||||
import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { ENDPOINT_TRUSTED_APPS_LIST_ID } from '@kbn/securitysolution-list-constants';
|
||||
import { isEmpty } from 'lodash/fp';
|
||||
import { ExceptionListClient } from '../../../../../lists/server';
|
||||
|
||||
import {
|
||||
|
@ -27,7 +29,38 @@ import {
|
|||
osFromExceptionItem,
|
||||
updatedTrustedAppToUpdateExceptionListItemOptions,
|
||||
} from './mapping';
|
||||
import { TrustedAppNotFoundError, TrustedAppVersionConflictError } from './errors';
|
||||
import {
|
||||
TrustedAppNotFoundError,
|
||||
TrustedAppVersionConflictError,
|
||||
TrustedAppPolicyNotExistsError,
|
||||
} from './errors';
|
||||
import { PackagePolicyServiceInterface } from '../../../../../fleet/server';
|
||||
import { PackagePolicy } from '../../../../../fleet/common';
|
||||
|
||||
const getNonExistingPoliciesFromTrustedApp = async (
|
||||
savedObjectClient: SavedObjectsClientContract,
|
||||
packagePolicyClient: PackagePolicyServiceInterface,
|
||||
trustedApp: PutTrustedAppUpdateRequest | PostTrustedAppCreateRequest
|
||||
): Promise<PackagePolicy[]> => {
|
||||
if (
|
||||
!trustedApp.effectScope ||
|
||||
trustedApp.effectScope.type === 'global' ||
|
||||
(trustedApp.effectScope.type === 'policy' && isEmpty(trustedApp.effectScope.policies))
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const policies = await packagePolicyClient.getByIDs(
|
||||
savedObjectClient,
|
||||
trustedApp.effectScope.policies
|
||||
);
|
||||
|
||||
if (!policies) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return policies.filter((policy) => policy.version === undefined);
|
||||
};
|
||||
|
||||
export const deleteTrustedApp = async (
|
||||
exceptionsListClient: ExceptionListClient,
|
||||
|
@ -90,13 +123,25 @@ export const getTrustedAppsList = async (
|
|||
|
||||
export const createTrustedApp = async (
|
||||
exceptionsListClient: ExceptionListClient,
|
||||
savedObjectClient: SavedObjectsClientContract,
|
||||
packagePolicyClient: PackagePolicyServiceInterface,
|
||||
newTrustedApp: PostTrustedAppCreateRequest
|
||||
): Promise<PostTrustedAppCreateResponse> => {
|
||||
// Ensure list is created if it does not exist
|
||||
await exceptionsListClient.createTrustedAppsList();
|
||||
|
||||
// Validate update TA entry - error if not valid
|
||||
// TODO: implement validations
|
||||
const unexistingPolicies = await getNonExistingPoliciesFromTrustedApp(
|
||||
savedObjectClient,
|
||||
packagePolicyClient,
|
||||
newTrustedApp
|
||||
);
|
||||
|
||||
if (!isEmpty(unexistingPolicies)) {
|
||||
throw new TrustedAppPolicyNotExistsError(
|
||||
newTrustedApp.name,
|
||||
unexistingPolicies.map((policy) => policy.id)
|
||||
);
|
||||
}
|
||||
|
||||
const createdTrustedAppExceptionItem = await exceptionsListClient.createExceptionListItem(
|
||||
newTrustedAppToCreateExceptionListItemOptions(newTrustedApp)
|
||||
|
@ -107,6 +152,8 @@ export const createTrustedApp = async (
|
|||
|
||||
export const updateTrustedApp = async (
|
||||
exceptionsListClient: ExceptionListClient,
|
||||
savedObjectClient: SavedObjectsClientContract,
|
||||
packagePolicyClient: PackagePolicyServiceInterface,
|
||||
id: string,
|
||||
updatedTrustedApp: PutTrustedAppUpdateRequest
|
||||
): Promise<PutTrustedAppUpdateResponse> => {
|
||||
|
@ -120,8 +167,18 @@ export const updateTrustedApp = async (
|
|||
throw new TrustedAppNotFoundError(id);
|
||||
}
|
||||
|
||||
// Validate update TA entry - error if not valid
|
||||
// TODO: implement validations
|
||||
const unexistingPolicies = await getNonExistingPoliciesFromTrustedApp(
|
||||
savedObjectClient,
|
||||
packagePolicyClient,
|
||||
updatedTrustedApp
|
||||
);
|
||||
|
||||
if (!isEmpty(unexistingPolicies)) {
|
||||
throw new TrustedAppPolicyNotExistsError(
|
||||
updatedTrustedApp.name,
|
||||
unexistingPolicies.map((policy) => policy.id)
|
||||
);
|
||||
}
|
||||
|
||||
let updatedTrustedAppExceptionItem: ExceptionListItemSchema | null;
|
||||
|
||||
|
|
Loading…
Reference in a new issue