mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
[Security Solution] [Endpoint] Generate empty endpoint user artifacts depending on the PLI (#163602)
## Summary Generates empty array when the PLI don't meet the requirement. It end up having empty fleet artifacts for those cannot be generated. It also adds new test cases --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
81a151ef71
commit
bc988f22c6
4 changed files with 295 additions and 8 deletions
|
@ -27,6 +27,9 @@ import { createEndpointArtifactClientMock, getManifestClientMock } from '../mock
|
|||
import type { ManifestManagerContext } from './manifest_manager';
|
||||
import { ManifestManager } from './manifest_manager';
|
||||
import { parseExperimentalConfigValue } from '../../../../../common/experimental_features';
|
||||
import { createAppFeaturesMock } from '../../../../lib/app_features/mocks';
|
||||
import type { AppFeatureKeys } from '../../../../../common/types/app_features';
|
||||
import type { AppFeatures } from '../../../../lib/app_features/app_features';
|
||||
|
||||
export const createExceptionListResponse = (data: ExceptionListItemSchema[], total?: number) => ({
|
||||
data,
|
||||
|
@ -68,24 +71,28 @@ export interface ManifestManagerMockOptions {
|
|||
exceptionListClient: ExceptionListClient;
|
||||
packagePolicyService: jest.Mocked<PackagePolicyClient>;
|
||||
savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
|
||||
appFeatures: AppFeatures;
|
||||
}
|
||||
|
||||
export const buildManifestManagerMockOptions = (
|
||||
opts: Partial<ManifestManagerMockOptions>
|
||||
opts: Partial<ManifestManagerMockOptions>,
|
||||
customAppFeatures?: AppFeatureKeys
|
||||
): ManifestManagerMockOptions => {
|
||||
const savedObjectMock = savedObjectsClientMock.create();
|
||||
return {
|
||||
exceptionListClient: listMock.getExceptionListClient(savedObjectMock),
|
||||
packagePolicyService: createPackagePolicyServiceMock(),
|
||||
savedObjectsClient: savedObjectMock,
|
||||
appFeatures: createAppFeaturesMock(customAppFeatures),
|
||||
...opts,
|
||||
};
|
||||
};
|
||||
|
||||
export const buildManifestManagerContextMock = (
|
||||
opts: Partial<ManifestManagerMockOptions>
|
||||
opts: Partial<ManifestManagerMockOptions>,
|
||||
customAppFeatures?: AppFeatureKeys
|
||||
): ManifestManagerContext => {
|
||||
const fullOpts = buildManifestManagerMockOptions(opts);
|
||||
const fullOpts = buildManifestManagerMockOptions(opts, customAppFeatures);
|
||||
|
||||
return {
|
||||
...fullOpts,
|
||||
|
|
|
@ -43,6 +43,7 @@ import type { EndpointArtifactClientInterface } from '../artifact_client';
|
|||
import { InvalidInternalManifestError } from '../errors';
|
||||
import { EndpointError } from '../../../../../common/endpoint/errors';
|
||||
import type { Artifact } from '@kbn/fleet-plugin/server';
|
||||
import { AppFeatureSecurityKey } from '../../../../../common/types/app_features';
|
||||
|
||||
const getArtifactObject = (artifact: InternalArtifactSchema) =>
|
||||
JSON.parse(Buffer.from(artifact.body!, 'base64').toString());
|
||||
|
@ -599,6 +600,271 @@ describe('ManifestManager', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('buildNewManifest when using app features', () => {
|
||||
const SUPPORTED_ARTIFACT_NAMES = [
|
||||
ARTIFACT_NAME_EXCEPTIONS_MACOS,
|
||||
ARTIFACT_NAME_EXCEPTIONS_WINDOWS,
|
||||
ARTIFACT_NAME_EXCEPTIONS_LINUX,
|
||||
ARTIFACT_NAME_TRUSTED_APPS_MACOS,
|
||||
ARTIFACT_NAME_TRUSTED_APPS_WINDOWS,
|
||||
ARTIFACT_NAME_TRUSTED_APPS_LINUX,
|
||||
ARTIFACT_NAME_EVENT_FILTERS_MACOS,
|
||||
ARTIFACT_NAME_EVENT_FILTERS_WINDOWS,
|
||||
ARTIFACT_NAME_EVENT_FILTERS_LINUX,
|
||||
ARTIFACT_NAME_HOST_ISOLATION_EXCEPTIONS_MACOS,
|
||||
ARTIFACT_NAME_HOST_ISOLATION_EXCEPTIONS_WINDOWS,
|
||||
ARTIFACT_NAME_HOST_ISOLATION_EXCEPTIONS_LINUX,
|
||||
ARTIFACT_NAME_BLOCKLISTS_MACOS,
|
||||
ARTIFACT_NAME_BLOCKLISTS_WINDOWS,
|
||||
ARTIFACT_NAME_BLOCKLISTS_LINUX,
|
||||
];
|
||||
|
||||
const getArtifactIds = (artifacts: InternalArtifactSchema[]) => [
|
||||
...new Set(artifacts.map((artifact) => artifact.identifier)).values(),
|
||||
];
|
||||
|
||||
const mockPolicyListIdsResponse = (items: string[]) =>
|
||||
jest.fn().mockResolvedValue({
|
||||
items,
|
||||
page: 1,
|
||||
per_page: 100,
|
||||
total: items.length,
|
||||
});
|
||||
|
||||
test('when it has endpoint artifact management app feature it should not generate host isolation exceptions', async () => {
|
||||
const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] });
|
||||
const trustedAppListItem = getExceptionListItemSchemaMock({
|
||||
os_types: ['linux'],
|
||||
tags: ['policy:all'],
|
||||
});
|
||||
const eventFiltersListItem = getExceptionListItemSchemaMock({
|
||||
os_types: ['windows'],
|
||||
tags: ['policy:all'],
|
||||
});
|
||||
const hostIsolationExceptionsItem = getExceptionListItemSchemaMock({
|
||||
os_types: ['linux'],
|
||||
tags: ['policy:all'],
|
||||
});
|
||||
const blocklistsListItem = getExceptionListItemSchemaMock({
|
||||
os_types: ['macos'],
|
||||
tags: ['policy:all'],
|
||||
});
|
||||
const context = buildManifestManagerContextMock({}, [
|
||||
AppFeatureSecurityKey.endpointArtifactManagement,
|
||||
]);
|
||||
const manifestManager = new ManifestManager(context);
|
||||
|
||||
context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({
|
||||
[ENDPOINT_LIST_ID]: { macos: [exceptionListItem] },
|
||||
[ENDPOINT_TRUSTED_APPS_LIST_ID]: { linux: [trustedAppListItem] },
|
||||
[ENDPOINT_EVENT_FILTERS_LIST_ID]: { linux: [eventFiltersListItem] },
|
||||
[ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID]: { linux: [hostIsolationExceptionsItem] },
|
||||
[ENDPOINT_BLOCKLISTS_LIST_ID]: { linux: [blocklistsListItem] },
|
||||
});
|
||||
context.savedObjectsClient.create = jest
|
||||
.fn()
|
||||
.mockImplementation((_type: string, object: InternalManifestSchema) => ({
|
||||
attributes: object,
|
||||
}));
|
||||
context.packagePolicyService.listIds = mockPolicyListIdsResponse([TEST_POLICY_ID_1]);
|
||||
|
||||
const manifest = await manifestManager.buildNewManifest();
|
||||
|
||||
expect(manifest?.getSchemaVersion()).toStrictEqual('v1');
|
||||
expect(manifest?.getSemanticVersion()).toStrictEqual('1.0.0');
|
||||
expect(manifest?.getSavedObjectVersion()).toBeUndefined();
|
||||
|
||||
const artifacts = manifest.getAllArtifacts();
|
||||
|
||||
expect(artifacts.length).toBe(15);
|
||||
expect(getArtifactIds(artifacts)).toStrictEqual(SUPPORTED_ARTIFACT_NAMES);
|
||||
|
||||
expect(getArtifactObject(artifacts[0])).toStrictEqual({
|
||||
entries: translateToEndpointExceptions([exceptionListItem], 'v1'),
|
||||
});
|
||||
expect(getArtifactObject(artifacts[1])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[2])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[3])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[4])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[5])).toStrictEqual({
|
||||
entries: translateToEndpointExceptions([trustedAppListItem], 'v1'),
|
||||
});
|
||||
expect(getArtifactObject(artifacts[6])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[7])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[8])).toStrictEqual({
|
||||
entries: translateToEndpointExceptions([eventFiltersListItem], 'v1'),
|
||||
});
|
||||
expect(getArtifactObject(artifacts[9])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[10])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[11])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[12])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[13])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[14])).toStrictEqual({
|
||||
entries: translateToEndpointExceptions([blocklistsListItem], 'v1'),
|
||||
});
|
||||
|
||||
for (const artifact of artifacts) {
|
||||
expect(manifest.isDefaultArtifact(artifact)).toBe(true);
|
||||
expect(manifest.getArtifactTargetPolicies(artifact)).toStrictEqual(
|
||||
new Set([TEST_POLICY_ID_1])
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('when it has endpoint artifact management and response actions app features it should generate all exceptions', async () => {
|
||||
const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] });
|
||||
const trustedAppListItem = getExceptionListItemSchemaMock({
|
||||
os_types: ['linux'],
|
||||
tags: ['policy:all'],
|
||||
});
|
||||
const eventFiltersListItem = getExceptionListItemSchemaMock({
|
||||
os_types: ['windows'],
|
||||
tags: ['policy:all'],
|
||||
});
|
||||
const hostIsolationExceptionsItem = getExceptionListItemSchemaMock({
|
||||
os_types: ['linux'],
|
||||
tags: ['policy:all'],
|
||||
});
|
||||
const blocklistsListItem = getExceptionListItemSchemaMock({
|
||||
os_types: ['macos'],
|
||||
tags: ['policy:all'],
|
||||
});
|
||||
const context = buildManifestManagerContextMock({}, [
|
||||
AppFeatureSecurityKey.endpointArtifactManagement,
|
||||
AppFeatureSecurityKey.endpointResponseActions,
|
||||
]);
|
||||
const manifestManager = new ManifestManager(context);
|
||||
|
||||
context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({
|
||||
[ENDPOINT_LIST_ID]: { macos: [exceptionListItem] },
|
||||
[ENDPOINT_TRUSTED_APPS_LIST_ID]: { linux: [trustedAppListItem] },
|
||||
[ENDPOINT_EVENT_FILTERS_LIST_ID]: { linux: [eventFiltersListItem] },
|
||||
[ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID]: { linux: [hostIsolationExceptionsItem] },
|
||||
[ENDPOINT_BLOCKLISTS_LIST_ID]: { linux: [blocklistsListItem] },
|
||||
});
|
||||
context.savedObjectsClient.create = jest
|
||||
.fn()
|
||||
.mockImplementation((_type: string, object: InternalManifestSchema) => ({
|
||||
attributes: object,
|
||||
}));
|
||||
context.packagePolicyService.listIds = mockPolicyListIdsResponse([TEST_POLICY_ID_1]);
|
||||
|
||||
const manifest = await manifestManager.buildNewManifest();
|
||||
|
||||
expect(manifest?.getSchemaVersion()).toStrictEqual('v1');
|
||||
expect(manifest?.getSemanticVersion()).toStrictEqual('1.0.0');
|
||||
expect(manifest?.getSavedObjectVersion()).toBeUndefined();
|
||||
|
||||
const artifacts = manifest.getAllArtifacts();
|
||||
|
||||
expect(artifacts.length).toBe(15);
|
||||
expect(getArtifactIds(artifacts)).toStrictEqual(SUPPORTED_ARTIFACT_NAMES);
|
||||
|
||||
expect(getArtifactObject(artifacts[0])).toStrictEqual({
|
||||
entries: translateToEndpointExceptions([exceptionListItem], 'v1'),
|
||||
});
|
||||
expect(getArtifactObject(artifacts[1])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[2])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[3])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[4])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[5])).toStrictEqual({
|
||||
entries: translateToEndpointExceptions([trustedAppListItem], 'v1'),
|
||||
});
|
||||
expect(getArtifactObject(artifacts[6])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[7])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[8])).toStrictEqual({
|
||||
entries: translateToEndpointExceptions([eventFiltersListItem], 'v1'),
|
||||
});
|
||||
expect(getArtifactObject(artifacts[9])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[10])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[11])).toStrictEqual({
|
||||
entries: translateToEndpointExceptions([hostIsolationExceptionsItem], 'v1'),
|
||||
});
|
||||
expect(getArtifactObject(artifacts[12])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[13])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[14])).toStrictEqual({
|
||||
entries: translateToEndpointExceptions([blocklistsListItem], 'v1'),
|
||||
});
|
||||
|
||||
for (const artifact of artifacts) {
|
||||
expect(manifest.isDefaultArtifact(artifact)).toBe(true);
|
||||
expect(manifest.getArtifactTargetPolicies(artifact)).toStrictEqual(
|
||||
new Set([TEST_POLICY_ID_1])
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('when does not have right app features, should not generate any exception', async () => {
|
||||
const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] });
|
||||
const trustedAppListItem = getExceptionListItemSchemaMock({
|
||||
os_types: ['linux'],
|
||||
tags: ['policy:all'],
|
||||
});
|
||||
const eventFiltersListItem = getExceptionListItemSchemaMock({
|
||||
os_types: ['windows'],
|
||||
tags: ['policy:all'],
|
||||
});
|
||||
const hostIsolationExceptionsItem = getExceptionListItemSchemaMock({
|
||||
os_types: ['linux'],
|
||||
tags: ['policy:all'],
|
||||
});
|
||||
const blocklistsListItem = getExceptionListItemSchemaMock({
|
||||
os_types: ['macos'],
|
||||
tags: ['policy:all'],
|
||||
});
|
||||
const context = buildManifestManagerContextMock({}, []);
|
||||
const manifestManager = new ManifestManager(context);
|
||||
|
||||
context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({
|
||||
[ENDPOINT_LIST_ID]: { macos: [exceptionListItem] },
|
||||
[ENDPOINT_TRUSTED_APPS_LIST_ID]: { linux: [trustedAppListItem] },
|
||||
[ENDPOINT_EVENT_FILTERS_LIST_ID]: { linux: [eventFiltersListItem] },
|
||||
[ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID]: { linux: [hostIsolationExceptionsItem] },
|
||||
[ENDPOINT_BLOCKLISTS_LIST_ID]: { linux: [blocklistsListItem] },
|
||||
});
|
||||
context.savedObjectsClient.create = jest
|
||||
.fn()
|
||||
.mockImplementation((_type: string, object: InternalManifestSchema) => ({
|
||||
attributes: object,
|
||||
}));
|
||||
context.packagePolicyService.listIds = mockPolicyListIdsResponse([TEST_POLICY_ID_1]);
|
||||
|
||||
const manifest = await manifestManager.buildNewManifest();
|
||||
|
||||
expect(manifest?.getSchemaVersion()).toStrictEqual('v1');
|
||||
expect(manifest?.getSemanticVersion()).toStrictEqual('1.0.0');
|
||||
expect(manifest?.getSavedObjectVersion()).toBeUndefined();
|
||||
|
||||
const artifacts = manifest.getAllArtifacts();
|
||||
|
||||
expect(artifacts.length).toBe(15);
|
||||
expect(getArtifactIds(artifacts)).toStrictEqual(SUPPORTED_ARTIFACT_NAMES);
|
||||
|
||||
expect(getArtifactObject(artifacts[0])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[1])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[2])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[3])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[4])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[5])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[6])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[7])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[8])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[9])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[10])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[11])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[12])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[13])).toStrictEqual({ entries: [] });
|
||||
expect(getArtifactObject(artifacts[14])).toStrictEqual({ entries: [] });
|
||||
|
||||
for (const artifact of artifacts) {
|
||||
expect(manifest.isDefaultArtifact(artifact)).toBe(true);
|
||||
expect(manifest.getArtifactTargetPolicies(artifact)).toStrictEqual(
|
||||
new Set([TEST_POLICY_ID_1])
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteArtifacts', () => {
|
||||
test('Successfully invokes saved objects client', async () => {
|
||||
const context = buildManifestManagerContextMock({});
|
||||
|
|
|
@ -20,6 +20,8 @@ import type { ListResult, PackagePolicy } from '@kbn/fleet-plugin/common';
|
|||
import type { Artifact, PackagePolicyClient } from '@kbn/fleet-plugin/server';
|
||||
import type { ExceptionListClient } from '@kbn/lists-plugin/server';
|
||||
import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { AppFeatureKey } from '../../../../../common/types/app_features';
|
||||
import type { AppFeatures } from '../../../../lib/app_features';
|
||||
import type { ManifestSchemaVersion } from '../../../../../common/endpoint/schema/common';
|
||||
import type { ManifestSchema } from '../../../../../common/endpoint/schema/manifest';
|
||||
import { manifestDispatchSchema } from '../../../../../common/endpoint/schema/manifest';
|
||||
|
@ -97,6 +99,7 @@ export interface ManifestManagerContext {
|
|||
experimentalFeatures: ExperimentalFeatures;
|
||||
packagerTaskPackagePolicyUpdateBatchSize: number;
|
||||
esClient: ElasticsearchClient;
|
||||
appFeatures: AppFeatures;
|
||||
}
|
||||
|
||||
const getArtifactIds = (manifest: ManifestSchema) =>
|
||||
|
@ -118,6 +121,7 @@ export class ManifestManager {
|
|||
protected cachedExceptionsListsByOs: Map<string, ExceptionListItemSchema[]>;
|
||||
protected packagerTaskPackagePolicyUpdateBatchSize: number;
|
||||
protected esClient: ElasticsearchClient;
|
||||
protected appFeatures: AppFeatures;
|
||||
|
||||
constructor(context: ManifestManagerContext) {
|
||||
this.artifactClient = context.artifactClient;
|
||||
|
@ -131,6 +135,7 @@ export class ManifestManager {
|
|||
this.packagerTaskPackagePolicyUpdateBatchSize =
|
||||
context.packagerTaskPackagePolicyUpdateBatchSize;
|
||||
this.esClient = context.esClient;
|
||||
this.appFeatures = context.appFeatures;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -159,11 +164,19 @@ export class ManifestManager {
|
|||
schemaVersion: string;
|
||||
}): Promise<WrappedTranslatedExceptionList> {
|
||||
if (!this.cachedExceptionsListsByOs.has(`${listId}-${os}`)) {
|
||||
const itemsByListId = await getAllItemsFromEndpointExceptionList({
|
||||
elClient,
|
||||
os,
|
||||
listId,
|
||||
});
|
||||
let itemsByListId: ExceptionListItemSchema[] = [];
|
||||
if (
|
||||
(listId === ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID &&
|
||||
this.appFeatures.isEnabled(AppFeatureKey.endpointResponseActions)) ||
|
||||
(listId !== ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID &&
|
||||
this.appFeatures.isEnabled(AppFeatureKey.endpointArtifactManagement))
|
||||
) {
|
||||
itemsByListId = await getAllItemsFromEndpointExceptionList({
|
||||
elClient,
|
||||
os,
|
||||
listId,
|
||||
});
|
||||
}
|
||||
this.cachedExceptionsListsByOs.set(`${listId}-${os}`, itemsByListId);
|
||||
}
|
||||
|
||||
|
|
|
@ -457,6 +457,7 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
experimentalFeatures: config.experimentalFeatures,
|
||||
packagerTaskPackagePolicyUpdateBatchSize: config.packagerTaskPackagePolicyUpdateBatchSize,
|
||||
esClient: core.elasticsearch.client.asInternalUser,
|
||||
appFeatures: this.appFeatures,
|
||||
});
|
||||
|
||||
// Migrate artifacts to fleet and then start the manifest task after that is done
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue