mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Security solution] [Endpoint] Remove linked policy from trusted apps when removing endpoint integration (#108347)
* Remove policy from trusted app when this is removed from fleet * Fleet: run package delete external callbacks when the Agent Policy is deleted
This commit is contained in:
parent
8d1ebea7db
commit
79e63cc654
13 changed files with 340 additions and 24 deletions
|
@ -7,8 +7,7 @@
|
|||
|
||||
import { APMPlugin, APMRouteHandlerResources } from '../..';
|
||||
import {
|
||||
ExternalCallback,
|
||||
PostPackagePolicyDeleteCallback,
|
||||
PostPackagePolicyCreateCallback,
|
||||
PutPackagePolicyUpdateCallback,
|
||||
} from '../../../../fleet/server';
|
||||
import {
|
||||
|
@ -60,7 +59,9 @@ export async function registerFleetPolicyCallbacks({
|
|||
});
|
||||
}
|
||||
|
||||
type ExternalCallbackParams = Parameters<ExternalCallback[1]>;
|
||||
type ExternalCallbackParams =
|
||||
| Parameters<PostPackagePolicyCreateCallback>
|
||||
| Parameters<PutPackagePolicyUpdateCallback>;
|
||||
export type PackagePolicy = NewPackagePolicy | UpdatePackagePolicy;
|
||||
type Context = ExternalCallbackParams[1];
|
||||
type Request = ExternalCallbackParams[2];
|
||||
|
@ -81,7 +82,7 @@ function registerPackagePolicyExternalCallback({
|
|||
logger: NonNullable<APMPlugin['logger']>;
|
||||
}) {
|
||||
const callbackFn:
|
||||
| PostPackagePolicyDeleteCallback
|
||||
| PostPackagePolicyCreateCallback
|
||||
| PutPackagePolicyUpdateCallback = async (
|
||||
packagePolicy: PackagePolicy,
|
||||
context: Context,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { NewPackagePolicy, PackagePolicy } from './types';
|
||||
import type { NewPackagePolicy, PackagePolicy, DeletePackagePoliciesResponse } from './types';
|
||||
|
||||
export const createNewPackagePolicyMock = (): NewPackagePolicy => {
|
||||
return {
|
||||
|
@ -45,3 +45,14 @@ export const createPackagePolicyMock = (): PackagePolicy => {
|
|||
],
|
||||
};
|
||||
};
|
||||
|
||||
export const deletePackagePolicyMock = (): DeletePackagePoliciesResponse => {
|
||||
const newPackagePolicy = createNewPackagePolicyMock();
|
||||
return [
|
||||
{
|
||||
id: 'c6d16e42-c32d-4dce-8a88-113cfe276ad1',
|
||||
success: true,
|
||||
package: newPackagePolicy.package,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
|
|
@ -62,7 +62,7 @@ export const xpackMocks = {
|
|||
createRequestHandlerContext: createCoreRequestHandlerContextMock,
|
||||
};
|
||||
|
||||
export const createPackagePolicyServiceMock = () => {
|
||||
export const createPackagePolicyServiceMock = (): jest.Mocked<PackagePolicyServiceInterface> => {
|
||||
return {
|
||||
compilePackagePolicyInputs: jest.fn(),
|
||||
buildPackagePolicyFromPackage: jest.fn(),
|
||||
|
@ -75,10 +75,11 @@ export const createPackagePolicyServiceMock = () => {
|
|||
listIds: jest.fn(),
|
||||
update: jest.fn(),
|
||||
runExternalCallbacks: jest.fn(),
|
||||
runDeleteExternalCallbacks: jest.fn(),
|
||||
upgrade: jest.fn(),
|
||||
getUpgradeDryRunDiff: jest.fn(),
|
||||
getUpgradePackagePolicyInfo: jest.fn(),
|
||||
} as jest.Mocked<PackagePolicyServiceInterface>;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -181,6 +181,7 @@ export const deletePackagePolicyHandler: RequestHandler<
|
|||
} catch (error) {
|
||||
const logger = appContextService.getLogger();
|
||||
logger.error(`An error occurred executing external callback: ${error}`);
|
||||
logger.error(error);
|
||||
}
|
||||
return response.ok({
|
||||
body,
|
||||
|
|
|
@ -12,6 +12,9 @@ import type { AgentPolicy, NewAgentPolicy, Output } from '../types';
|
|||
import { agentPolicyService } from './agent_policy';
|
||||
import { agentPolicyUpdateEventHandler } from './agent_policy_update';
|
||||
|
||||
import { getAgentsByKuery } from './agents';
|
||||
import { packagePolicyService } from './package_policy';
|
||||
|
||||
function getSavedObjectMock(agentPolicyAttributes: any) {
|
||||
const mock = savedObjectsClientMock.create();
|
||||
mock.get.mockImplementation(async (type: string, id: string) => {
|
||||
|
@ -63,6 +66,8 @@ jest.mock('./output', () => {
|
|||
});
|
||||
|
||||
jest.mock('./agent_policy_update');
|
||||
jest.mock('./agents');
|
||||
jest.mock('./package_policy');
|
||||
|
||||
function getAgentPolicyUpdateMock() {
|
||||
return (agentPolicyUpdateEventHandler as unknown) as jest.Mock<
|
||||
|
@ -123,6 +128,36 @@ describe('agent policy', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('delete', () => {
|
||||
let soClient: ReturnType<typeof savedObjectsClientMock.create>;
|
||||
let esClient: ReturnType<typeof elasticsearchServiceMock.createClusterClient>['asInternalUser'];
|
||||
|
||||
beforeEach(() => {
|
||||
soClient = getSavedObjectMock({ revision: 1, package_policies: ['package-1'] });
|
||||
esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
|
||||
(getAgentsByKuery as jest.Mock).mockResolvedValue({
|
||||
agents: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
});
|
||||
|
||||
(packagePolicyService.delete as jest.Mock).mockResolvedValue([
|
||||
{
|
||||
id: 'package-1',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should run package policy delete external callbacks', async () => {
|
||||
await agentPolicyService.delete(soClient, esClient, 'mocked');
|
||||
expect(packagePolicyService.runDeleteExternalCallbacks).toHaveBeenCalledWith([
|
||||
{ id: 'package-1' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('bumpRevision', () => {
|
||||
it('should call agentPolicyUpdateEventHandler with updated event once', async () => {
|
||||
const soClient = getSavedObjectMock({
|
||||
|
|
|
@ -45,6 +45,7 @@ import type {
|
|||
FleetServerPolicy,
|
||||
Installation,
|
||||
Output,
|
||||
DeletePackagePoliciesResponse,
|
||||
} from '../../common';
|
||||
import { AgentPolicyNameExistsError, HostedAgentPolicyRestrictionRelatedError } from '../errors';
|
||||
import {
|
||||
|
@ -616,7 +617,7 @@ class AgentPolicyService {
|
|||
}
|
||||
|
||||
if (agentPolicy.package_policies && agentPolicy.package_policies.length) {
|
||||
await packagePolicyService.delete(
|
||||
const deletedPackagePolicies: DeletePackagePoliciesResponse = await packagePolicyService.delete(
|
||||
soClient,
|
||||
esClient,
|
||||
agentPolicy.package_policies as string[],
|
||||
|
@ -624,6 +625,13 @@ class AgentPolicyService {
|
|||
skipUnassignFromAgentPolicies: true,
|
||||
}
|
||||
);
|
||||
try {
|
||||
await packagePolicyService.runDeleteExternalCallbacks(deletedPackagePolicies);
|
||||
} catch (error) {
|
||||
const logger = appContextService.getLogger();
|
||||
logger.error(`An error occurred executing external callback: ${error}`);
|
||||
logger.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (agentPolicy.is_preconfigured) {
|
||||
|
|
|
@ -20,6 +20,12 @@ import type { PutPackagePolicyUpdateCallback, PostPackagePolicyCreateCallback }
|
|||
|
||||
import { createAppContextStartContractMock, xpackMocks } from '../mocks';
|
||||
|
||||
import type { PostPackagePolicyDeleteCallback } from '../types';
|
||||
|
||||
import type { DeletePackagePoliciesResponse } from '../../common';
|
||||
|
||||
import { IngestManagerError } from '../errors';
|
||||
|
||||
import { packagePolicyService } from './package_policy';
|
||||
import { appContextService } from './app_context';
|
||||
|
||||
|
@ -815,6 +821,78 @@ describe('Package policy service', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('runDeleteExternalCallbacks', () => {
|
||||
let callbackOne: jest.MockedFunction<PostPackagePolicyDeleteCallback>;
|
||||
let callbackTwo: jest.MockedFunction<PostPackagePolicyDeleteCallback>;
|
||||
let callingOrder: string[];
|
||||
let deletedPackagePolicies: DeletePackagePoliciesResponse;
|
||||
|
||||
beforeEach(() => {
|
||||
appContextService.start(createAppContextStartContractMock());
|
||||
callingOrder = [];
|
||||
deletedPackagePolicies = [
|
||||
{ id: 'a', success: true },
|
||||
{ id: 'a', success: true },
|
||||
];
|
||||
callbackOne = jest.fn(async (deletedPolicies) => {
|
||||
callingOrder.push('one');
|
||||
});
|
||||
callbackTwo = jest.fn(async (deletedPolicies) => {
|
||||
callingOrder.push('two');
|
||||
});
|
||||
appContextService.addExternalCallback('postPackagePolicyDelete', callbackOne);
|
||||
appContextService.addExternalCallback('postPackagePolicyDelete', callbackTwo);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
appContextService.stop();
|
||||
});
|
||||
|
||||
it('should execute external callbacks', async () => {
|
||||
await packagePolicyService.runDeleteExternalCallbacks(deletedPackagePolicies);
|
||||
|
||||
expect(callbackOne).toHaveBeenCalledWith(deletedPackagePolicies);
|
||||
expect(callbackTwo).toHaveBeenCalledWith(deletedPackagePolicies);
|
||||
expect(callingOrder).toEqual(['one', 'two']);
|
||||
});
|
||||
|
||||
it("should execute all external callbacks even if one throw's", async () => {
|
||||
callbackOne.mockImplementation(async (deletedPolicies) => {
|
||||
callingOrder.push('one');
|
||||
throw new Error('foo');
|
||||
});
|
||||
await expect(
|
||||
packagePolicyService.runDeleteExternalCallbacks(deletedPackagePolicies)
|
||||
).rejects.toThrow(IngestManagerError);
|
||||
expect(callingOrder).toEqual(['one', 'two']);
|
||||
});
|
||||
|
||||
it('should provide an array of errors encountered by running external callbacks', async () => {
|
||||
let error: IngestManagerError;
|
||||
const callbackOneError = new Error('foo 1');
|
||||
const callbackTwoError = new Error('foo 2');
|
||||
|
||||
callbackOne.mockImplementation(async (deletedPolicies) => {
|
||||
callingOrder.push('one');
|
||||
throw callbackOneError;
|
||||
});
|
||||
callbackTwo.mockImplementation(async (deletedPolicies) => {
|
||||
callingOrder.push('two');
|
||||
throw callbackTwoError;
|
||||
});
|
||||
|
||||
await packagePolicyService.runDeleteExternalCallbacks(deletedPackagePolicies).catch((e) => {
|
||||
error = e;
|
||||
});
|
||||
|
||||
expect(error!.message).toEqual(
|
||||
'2 encountered while executing package delete external callbacks'
|
||||
);
|
||||
expect(error!.meta).toEqual([callbackOneError, callbackTwoError]);
|
||||
expect(callingOrder).toEqual(['one', 'two']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('runExternalCallbacks', () => {
|
||||
let context: ReturnType<typeof xpackMocks.createRequestHandlerContext>;
|
||||
let request: KibanaRequest;
|
||||
|
|
|
@ -428,9 +428,9 @@ class PackagePolicyService {
|
|||
name: packagePolicy.name,
|
||||
success: true,
|
||||
package: {
|
||||
name: packagePolicy.name,
|
||||
title: '',
|
||||
version: packagePolicy.version || '',
|
||||
name: packagePolicy.package?.name || '',
|
||||
title: packagePolicy.package?.title || '',
|
||||
version: packagePolicy.package?.version || '',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
|
@ -642,7 +642,9 @@ class PackagePolicyService {
|
|||
|
||||
public async runExternalCallbacks<A extends ExternalCallback[0]>(
|
||||
externalCallbackType: A,
|
||||
packagePolicy: NewPackagePolicy | DeletePackagePoliciesResponse,
|
||||
packagePolicy: A extends 'postPackagePolicyDelete'
|
||||
? DeletePackagePoliciesResponse
|
||||
: NewPackagePolicy,
|
||||
context: RequestHandlerContext,
|
||||
request: KibanaRequest
|
||||
): Promise<A extends 'postPackagePolicyDelete' ? void : NewPackagePolicy>;
|
||||
|
@ -653,14 +655,7 @@ class PackagePolicyService {
|
|||
request: KibanaRequest
|
||||
): Promise<NewPackagePolicy | void> {
|
||||
if (externalCallbackType === 'postPackagePolicyDelete') {
|
||||
const externalCallbacks = appContextService.getExternalCallbacks(externalCallbackType);
|
||||
if (externalCallbacks && externalCallbacks.size > 0) {
|
||||
for (const callback of externalCallbacks) {
|
||||
if (Array.isArray(packagePolicy)) {
|
||||
await callback(packagePolicy, context, request);
|
||||
}
|
||||
}
|
||||
}
|
||||
return await this.runDeleteExternalCallbacks(packagePolicy as DeletePackagePoliciesResponse);
|
||||
} else {
|
||||
if (!Array.isArray(packagePolicy)) {
|
||||
let newData = packagePolicy;
|
||||
|
@ -682,6 +677,32 @@ class PackagePolicyService {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async runDeleteExternalCallbacks(
|
||||
deletedPackagePolicies: DeletePackagePoliciesResponse
|
||||
): Promise<void> {
|
||||
const externalCallbacks = appContextService.getExternalCallbacks('postPackagePolicyDelete');
|
||||
const errorsThrown: Error[] = [];
|
||||
|
||||
if (externalCallbacks && externalCallbacks.size > 0) {
|
||||
for (const callback of externalCallbacks) {
|
||||
// Failures from an external callback should not prevent other external callbacks from being
|
||||
// executed. Errors (if any) will be collected and `throw`n after processing the entire set
|
||||
try {
|
||||
await callback(deletedPackagePolicies);
|
||||
} catch (error) {
|
||||
errorsThrown.push(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (errorsThrown.length > 0) {
|
||||
throw new IngestManagerError(
|
||||
`${errorsThrown.length} encountered while executing package delete external callbacks`,
|
||||
errorsThrown
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function assignStreamIdToInput(packagePolicyId: string, input: NewPackagePolicyInput) {
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
import type { KibanaRequest, RequestHandlerContext } from 'kibana/server';
|
||||
|
||||
import type { DeepReadonly } from 'utility-types';
|
||||
|
||||
import type {
|
||||
DeletePackagePoliciesResponse,
|
||||
NewPackagePolicy,
|
||||
|
@ -14,9 +16,7 @@ import type {
|
|||
} from '../../common';
|
||||
|
||||
export type PostPackagePolicyDeleteCallback = (
|
||||
deletedPackagePolicies: DeletePackagePoliciesResponse,
|
||||
context: RequestHandlerContext,
|
||||
request: KibanaRequest
|
||||
deletedPackagePolicies: DeepReadonly<DeletePackagePoliciesResponse>
|
||||
) => Promise<void>;
|
||||
|
||||
export type PostPackagePolicyCreateCallback = (
|
||||
|
|
|
@ -22,6 +22,7 @@ import { PluginStartContract as AlertsPluginStartContract } from '../../../alert
|
|||
import {
|
||||
getPackagePolicyCreateCallback,
|
||||
getPackagePolicyUpdateCallback,
|
||||
getPackagePolicyDeleteCallback,
|
||||
} from '../fleet_integration/fleet_integration';
|
||||
import { ManifestManager } from './services/artifacts';
|
||||
import { AppClientFactory } from '../client';
|
||||
|
@ -102,6 +103,11 @@ export class EndpointAppContextService {
|
|||
'packagePolicyUpdate',
|
||||
getPackagePolicyUpdateCallback(dependencies.logger, dependencies.licenseService)
|
||||
);
|
||||
|
||||
dependencies.registerIngestCallback(
|
||||
'postPackagePolicyDelete',
|
||||
getPackagePolicyDeleteCallback(dependencies.exceptionListsClient, this.experimentalFeatures)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { httpServerMock, loggingSystemMock } from 'src/core/server/mocks';
|
||||
import { createNewPackagePolicyMock } from '../../../fleet/common/mocks';
|
||||
import { createNewPackagePolicyMock, deletePackagePolicyMock } from '../../../fleet/common/mocks';
|
||||
import {
|
||||
policyFactory,
|
||||
policyFactoryWithoutPaidFeatures,
|
||||
|
@ -14,6 +14,7 @@ import {
|
|||
import { buildManifestManagerMock } from '../endpoint/services/artifacts/manifest_manager/manifest_manager.mock';
|
||||
import {
|
||||
getPackagePolicyCreateCallback,
|
||||
getPackagePolicyDeleteCallback,
|
||||
getPackagePolicyUpdateCallback,
|
||||
} from './fleet_integration';
|
||||
import { KibanaRequest } from 'kibana/server';
|
||||
|
@ -28,6 +29,7 @@ import { EndpointDocGenerator } from '../../common/endpoint/generate_data';
|
|||
import { ProtectionModes } from '../../common/endpoint/types';
|
||||
import type { SecuritySolutionRequestHandlerContext } from '../types';
|
||||
import { getExceptionListClientMock } from '../../../lists/server/services/exception_lists/exception_list_client.mock';
|
||||
import { getExceptionListSchemaMock } from '../../../lists/common/schemas/response/exception_list_schema.mock';
|
||||
import { ExceptionListClient } from '../../../lists/server';
|
||||
import { InternalArtifactCompleteSchema } from '../endpoint/schemas/artifacts';
|
||||
import { ManifestManager } from '../endpoint/services/artifacts/manifest_manager';
|
||||
|
@ -35,6 +37,12 @@ import { getMockArtifacts, toArtifactRecords } from '../endpoint/lib/artifacts/m
|
|||
import { Manifest } from '../endpoint/lib/artifacts';
|
||||
import { NewPackagePolicy } from '../../../fleet/common/types/models';
|
||||
import { ManifestSchema } from '../../common/endpoint/schema/manifest';
|
||||
import {
|
||||
allowedExperimentalValues,
|
||||
ExperimentalFeatures,
|
||||
} from '../../common/experimental_features';
|
||||
import { DeletePackagePoliciesResponse } from '../../../fleet/common';
|
||||
import { ExceptionListSchema } from '@kbn/securitysolution-io-ts-list-types';
|
||||
|
||||
describe('ingest_integration tests ', () => {
|
||||
let endpointAppContextMock: EndpointAppContextServiceStartContract;
|
||||
|
@ -282,4 +290,66 @@ describe('ingest_integration tests ', () => {
|
|||
expect(updatedPolicyConfig.inputs[0]!.config!.policy.value).toEqual(mockPolicy);
|
||||
});
|
||||
});
|
||||
|
||||
describe('package policy delete callback with trusted apps by policy enabled', () => {
|
||||
const invokeDeleteCallback = async (
|
||||
experimentalFeatures?: ExperimentalFeatures
|
||||
): Promise<void> => {
|
||||
const callback = getPackagePolicyDeleteCallback(exceptionListClient, experimentalFeatures);
|
||||
await callback(deletePackagePolicyMock());
|
||||
};
|
||||
|
||||
let removedPolicies: DeletePackagePoliciesResponse;
|
||||
let policyId: string;
|
||||
let fakeTA: ExceptionListSchema;
|
||||
|
||||
beforeEach(() => {
|
||||
removedPolicies = deletePackagePolicyMock();
|
||||
policyId = removedPolicies[0].id;
|
||||
fakeTA = {
|
||||
...getExceptionListSchemaMock(),
|
||||
tags: [`policy:${policyId}`],
|
||||
};
|
||||
|
||||
exceptionListClient.findExceptionListItem = jest
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ data: [fakeTA], total: 1 });
|
||||
exceptionListClient.updateExceptionListItem = jest
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ ...fakeTA, tags: [] });
|
||||
});
|
||||
|
||||
it('removes policy from trusted app FF enabled', async () => {
|
||||
await invokeDeleteCallback({
|
||||
...allowedExperimentalValues,
|
||||
trustedAppsByPolicyEnabled: true, // Needs to be enabled, it needs also a test with this disabled.
|
||||
});
|
||||
|
||||
expect(exceptionListClient.findExceptionListItem).toHaveBeenCalledWith({
|
||||
filter: `exception-list-agnostic.attributes.tags:"policy:${policyId}"`,
|
||||
listId: 'endpoint_trusted_apps',
|
||||
namespaceType: 'agnostic',
|
||||
page: 1,
|
||||
perPage: 50,
|
||||
sortField: undefined,
|
||||
sortOrder: undefined,
|
||||
});
|
||||
|
||||
expect(exceptionListClient.updateExceptionListItem).toHaveBeenCalledWith({
|
||||
...fakeTA,
|
||||
namespaceType: fakeTA.namespace_type,
|
||||
osTypes: fakeTA.os_types,
|
||||
tags: [],
|
||||
});
|
||||
});
|
||||
|
||||
it("doesn't remove policy from trusted app FF disabled", async () => {
|
||||
await invokeDeleteCallback({
|
||||
...allowedExperimentalValues,
|
||||
});
|
||||
|
||||
expect(exceptionListClient.findExceptionListItem).toHaveBeenCalledTimes(0);
|
||||
expect(exceptionListClient.updateExceptionListItem).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,9 +11,12 @@ import { PluginStartContract as AlertsStartContract } from '../../../alerting/se
|
|||
import { SecurityPluginStart } from '../../../security/server';
|
||||
import {
|
||||
PostPackagePolicyCreateCallback,
|
||||
PostPackagePolicyDeleteCallback,
|
||||
PutPackagePolicyUpdateCallback,
|
||||
} from '../../../fleet/server';
|
||||
|
||||
import { NewPackagePolicy, UpdatePackagePolicy } from '../../../fleet/common';
|
||||
|
||||
import { NewPolicyData, PolicyConfig } from '../../common/endpoint/types';
|
||||
import { ManifestManager } from '../endpoint/services';
|
||||
import { AppClientFactory } from '../client';
|
||||
|
@ -22,6 +25,8 @@ import { installPrepackagedRules } from './handlers/install_prepackaged_rules';
|
|||
import { createPolicyArtifactManifest } from './handlers/create_policy_artifact_manifest';
|
||||
import { createDefaultPolicy } from './handlers/create_default_policy';
|
||||
import { validatePolicyAgainstLicense } from './handlers/validate_policy_against_license';
|
||||
import { removePolicyFromTrustedApps } from './handlers/remove_policy_from_trusted_apps';
|
||||
import { ExperimentalFeatures } from '../../common/experimental_features';
|
||||
|
||||
const isEndpointPackagePolicy = <T extends { package?: { name: string } }>(
|
||||
packagePolicy: T
|
||||
|
@ -126,3 +131,21 @@ export const getPackagePolicyUpdateCallback = (
|
|||
return newPackagePolicy;
|
||||
};
|
||||
};
|
||||
|
||||
export const getPackagePolicyDeleteCallback = (
|
||||
exceptionsClient: ExceptionListClient | undefined,
|
||||
experimentalFeatures: ExperimentalFeatures | undefined
|
||||
): PostPackagePolicyDeleteCallback => {
|
||||
return async (deletePackagePolicy): Promise<void> => {
|
||||
if (!exceptionsClient) {
|
||||
return;
|
||||
}
|
||||
const policiesToRemove: Array<Promise<void>> = [];
|
||||
for (const policy of deletePackagePolicy) {
|
||||
if (isEndpointPackagePolicy(policy) && experimentalFeatures?.trustedAppsByPolicyEnabled) {
|
||||
policiesToRemove.push(removePolicyFromTrustedApps(exceptionsClient, policy));
|
||||
}
|
||||
}
|
||||
await Promise.all(policiesToRemove);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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_TRUSTED_APPS_LIST_ID } from '@kbn/securitysolution-list-constants';
|
||||
import { ExceptionListClient } from '../../../../lists/server';
|
||||
import { PostPackagePolicyDeleteCallback } from '../../../../fleet/server';
|
||||
|
||||
/**
|
||||
* Removes policy from trusted apps
|
||||
*/
|
||||
export const removePolicyFromTrustedApps = async (
|
||||
exceptionsClient: ExceptionListClient,
|
||||
policy: Parameters<PostPackagePolicyDeleteCallback>[0][0]
|
||||
) => {
|
||||
let page = 1;
|
||||
|
||||
const findTrustedAppsByPolicy = async (currentPage: number) => {
|
||||
return exceptionsClient.findExceptionListItem({
|
||||
listId: ENDPOINT_TRUSTED_APPS_LIST_ID,
|
||||
filter: `exception-list-agnostic.attributes.tags:"policy:${policy.id}"`,
|
||||
namespaceType: 'agnostic',
|
||||
page: currentPage,
|
||||
perPage: 50,
|
||||
sortField: undefined,
|
||||
sortOrder: undefined,
|
||||
});
|
||||
};
|
||||
|
||||
let findResponse = await findTrustedAppsByPolicy(page);
|
||||
if (!findResponse) {
|
||||
return;
|
||||
}
|
||||
const trustedApps = findResponse.data;
|
||||
|
||||
while (findResponse && (trustedApps.length < findResponse.total || findResponse.data.length)) {
|
||||
page += 1;
|
||||
findResponse = await findTrustedAppsByPolicy(page);
|
||||
if (findResponse) {
|
||||
trustedApps.push(...findResponse.data);
|
||||
}
|
||||
}
|
||||
|
||||
const updates = [];
|
||||
for (const trustedApp of trustedApps) {
|
||||
updates.push(
|
||||
exceptionsClient.updateExceptionListItem({
|
||||
...trustedApp,
|
||||
itemId: trustedApp.item_id,
|
||||
namespaceType: trustedApp.namespace_type,
|
||||
osTypes: trustedApp.os_types,
|
||||
tags: trustedApp.tags.filter((currentPolicy) => currentPolicy !== `policy:${policy.id}`),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
await Promise.all(updates);
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue