[EDR Workflows] Turn on new Manifest Manager feature flag. (#186596)

Turns on feature flag introduced in
https://github.com/elastic/kibana/pull/179698

Aligned tests - removed tests that were based on feature flag being
present or not.
This commit is contained in:
Konrad Szwarc 2024-07-01 21:08:44 +02:00 committed by GitHub
parent 804478bf7a
commit 414acd16a5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 151 additions and 2045 deletions

View file

@ -582,5 +582,3 @@ enabled:
- x-pack/test/security_solution_api_integration/test_suites/security_solution_endpoint/configs/serverless.endpoint.config.ts
- x-pack/test/security_solution_api_integration/test_suites/security_solution_endpoint/configs/integrations.config.ts
- x-pack/test/security_solution_api_integration/test_suites/security_solution_endpoint/configs/serverless.integrations.config.ts
- x-pack/test/security_solution_api_integration/test_suites/security_solution_endpoint/configs/serverless.integrations_feature_flag.config.ts
- x-pack/test/security_solution_api_integration/test_suites/security_solution_endpoint/configs/integrations_feature_flag.config.ts

View file

@ -232,7 +232,7 @@ export const allowedExperimentalValues = Object.freeze({
/**
* Enables unified manifest that replaces existing user artifacts manifest SO with a new approach of creating a SO per package policy.
*/
unifiedManifestEnabled: false,
unifiedManifestEnabled: true,
/**
* Enables Security AI Assistant's Flyout mode

View file

@ -36,7 +36,6 @@ import {
import type { ManifestManagerContext } from './manifest_manager';
import { ManifestManager } from './manifest_manager';
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 { ProductFeatureSecurityKey } from '@kbn/security-solution-features/keys';
@ -147,7 +146,6 @@ describe('ManifestManager', () => {
const manifestManager = new ManifestManager(
buildManifestManagerContextMock({
savedObjectsClient,
experimentalFeatures: ['unifiedManifestEnabled'],
})
);
@ -167,7 +165,6 @@ describe('ManifestManager', () => {
const manifestManager = new ManifestManager(
buildManifestManagerContextMock({
savedObjectsClient,
experimentalFeatures: ['unifiedManifestEnabled'],
})
);
@ -239,7 +236,6 @@ describe('ManifestManager', () => {
const savedObjectsClient = savedObjectsClientMock.create();
const manifestManagerContext = buildManifestManagerContextMock({
savedObjectsClient,
experimentalFeatures: ['unifiedManifestEnabled'],
});
const manifestManager = new ManifestManager(manifestManagerContext);
@ -271,186 +267,9 @@ describe('ManifestManager', () => {
});
});
describe('getLastComputedManifest', () => {
test('Returns null when saved object not found', async () => {
const savedObjectsClient = savedObjectsClientMock.create();
const manifestManager = new ManifestManager(
buildManifestManagerContextMock({ savedObjectsClient })
);
savedObjectsClient.get = jest.fn().mockRejectedValue({ output: { statusCode: 404 } });
expect(await manifestManager.getLastComputedManifest()).toBe(null);
});
test('Throws error when saved object client responds with 500', async () => {
const savedObjectsClient = savedObjectsClientMock.create();
const manifestManager = new ManifestManager(
buildManifestManagerContextMock({ savedObjectsClient })
);
const error = { message: 'bad request', output: { statusCode: 500 } };
savedObjectsClient.get = jest.fn().mockRejectedValue(error);
await expect(manifestManager.getLastComputedManifest()).rejects.toThrow(
new EndpointError('bad request', error)
);
});
test('Throws error when no version on the manifest', async () => {
const savedObjectsClient = savedObjectsClientMock.create();
const manifestManager = new ManifestManager(
buildManifestManagerContextMock({ savedObjectsClient })
);
savedObjectsClient.get = jest.fn().mockResolvedValue({});
await expect(manifestManager.getLastComputedManifest()).rejects.toStrictEqual(
new InvalidInternalManifestError('Internal Manifest map SavedObject is missing version')
);
});
test('Retrieves empty manifest successfully', async () => {
const savedObjectsClient = savedObjectsClientMock.create();
const manifestManager = new ManifestManager(
buildManifestManagerContextMock({ savedObjectsClient })
);
savedObjectsClient.get = jest.fn().mockResolvedValue({
attributes: {
created: '20-01-2020 10:00:00.000Z',
schemaVersion: 'v2',
semanticVersion: '1.0.0',
artifacts: [],
},
version: '2.0.0',
});
const manifest = await manifestManager.getLastComputedManifest();
expect(manifest?.getSchemaVersion()).toStrictEqual('v1');
expect(manifest?.getSemanticVersion()).toStrictEqual('1.0.0');
expect(manifest?.getSavedObjectVersion()).toStrictEqual('2.0.0');
expect(manifest?.getAllArtifacts()).toStrictEqual([]);
});
test('Retrieves non empty manifest successfully', async () => {
const savedObjectsClient = savedObjectsClientMock.create();
const manifestManagerContext = buildManifestManagerContextMock({ savedObjectsClient });
const manifestManager = new ManifestManager(manifestManagerContext);
savedObjectsClient.get = jest.fn().mockImplementation(async (objectType: string) => {
if (objectType === ManifestConstants.SAVED_OBJECT_TYPE) {
return {
attributes: {
created: '20-01-2020 10:00:00.000Z',
schemaVersion: 'v2',
semanticVersion: '1.0.0',
artifacts: [
{ artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined },
{ artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: undefined },
{ artifactId: ARTIFACT_ID_EXCEPTIONS_LINUX, policyId: undefined },
{ artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_1 },
{ artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 },
{ artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_1 },
{ artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_2 },
],
},
version: '2.0.0',
};
} else {
return null;
}
});
(
manifestManagerContext.artifactClient as jest.Mocked<EndpointArtifactClientInterface>
).fetchAll.mockReturnValue(createFetchAllArtifactsIterableMock([ARTIFACTS as Artifact[]]));
const manifest = await manifestManager.getLastComputedManifest();
expect(manifest?.getSchemaVersion()).toStrictEqual('v1');
expect(manifest?.getSemanticVersion()).toStrictEqual('1.0.0');
expect(manifest?.getSavedObjectVersion()).toStrictEqual('2.0.0');
expect(manifest?.getAllArtifacts()).toStrictEqual(ARTIFACTS.slice(0, 5));
expect(manifest?.isDefaultArtifact(ARTIFACT_EXCEPTIONS_MACOS)).toBe(true);
expect(manifest?.getArtifactTargetPolicies(ARTIFACT_EXCEPTIONS_MACOS)).toStrictEqual(
new Set()
);
expect(manifest?.isDefaultArtifact(ARTIFACT_EXCEPTIONS_WINDOWS)).toBe(true);
expect(manifest?.getArtifactTargetPolicies(ARTIFACT_EXCEPTIONS_WINDOWS)).toStrictEqual(
new Set([TEST_POLICY_ID_1])
);
expect(manifest?.isDefaultArtifact(ARTIFACT_TRUSTED_APPS_MACOS)).toBe(false);
expect(manifest?.getArtifactTargetPolicies(ARTIFACT_TRUSTED_APPS_MACOS)).toStrictEqual(
new Set([TEST_POLICY_ID_1])
);
expect(manifest?.isDefaultArtifact(ARTIFACT_TRUSTED_APPS_WINDOWS)).toBe(false);
expect(manifest?.getArtifactTargetPolicies(ARTIFACT_TRUSTED_APPS_WINDOWS)).toStrictEqual(
new Set([TEST_POLICY_ID_1, TEST_POLICY_ID_2])
);
});
test("Retrieve non empty manifest and skips over artifacts that can't be found", async () => {
const savedObjectsClient = savedObjectsClientMock.create();
const manifestManagerContext = buildManifestManagerContextMock({ savedObjectsClient });
const manifestManager = new ManifestManager(manifestManagerContext);
savedObjectsClient.get = jest.fn().mockImplementation(async (objectType: string) => {
if (objectType === ManifestConstants.SAVED_OBJECT_TYPE) {
return {
attributes: {
created: '20-01-2020 10:00:00.000Z',
schemaVersion: 'v2',
semanticVersion: '1.0.0',
artifacts: [
{ artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined },
{ artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: undefined },
{ artifactId: ARTIFACT_ID_EXCEPTIONS_LINUX, policyId: undefined },
{ artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_1 },
{ artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 },
{ artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_1 },
{ artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_2 },
],
},
version: '2.0.0',
};
} else {
return null;
}
});
(
manifestManagerContext.artifactClient as jest.Mocked<EndpointArtifactClientInterface>
).fetchAll.mockReturnValue(
createFetchAllArtifactsIterableMock([
// report the MACOS Exceptions artifact as not found
[
ARTIFACT_TRUSTED_APPS_MACOS,
ARTIFACT_EXCEPTIONS_WINDOWS,
ARTIFACT_TRUSTED_APPS_WINDOWS,
ARTIFACTS_BY_ID[ARTIFACT_ID_EXCEPTIONS_LINUX],
] as Artifact[],
])
);
const manifest = await manifestManager.getLastComputedManifest();
expect(manifest?.getAllArtifacts()).toStrictEqual(ARTIFACTS.slice(1, 5));
expect(manifestManagerContext.logger.warn).toHaveBeenCalledWith(
"Missing artifacts detected! Internal artifact manifest (SavedObject version [2.0.0]) references [1] artifact IDs that don't exist.\n" +
"First 10 below (run with logging set to 'debug' to see all):\n" +
'endpoint-exceptionlist-macos-v1-96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3'
);
});
});
describe('commit unified manifest', () => {
test('Correctly updates, creates and deletes unified manifest so', async () => {
const context = buildManifestManagerContextMock({
experimentalFeatures: ['unifiedManifestEnabled'],
});
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);
const manifest = ManifestManager.createDefaultManifest();
@ -536,107 +355,7 @@ describe('ManifestManager', () => {
});
});
describe('commit', () => {
test('Creates new saved object if no saved object version', async () => {
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);
const manifest = ManifestManager.createDefaultManifest();
manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS);
manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS, TEST_POLICY_ID_1);
manifest.addEntry(ARTIFACT_EXCEPTIONS_WINDOWS, TEST_POLICY_ID_2);
manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_1);
manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_2);
context.savedObjectsClient.create = jest
.fn()
.mockImplementation((_type: string, object: InternalManifestSchema) => object);
await expect(manifestManager.commit(manifest)).resolves.toBeUndefined();
expect(context.savedObjectsClient.create).toHaveBeenCalledTimes(1);
expect(context.savedObjectsClient.create).toHaveBeenNthCalledWith(
1,
ManifestConstants.SAVED_OBJECT_TYPE,
{
artifacts: [
{ artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined },
{ artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: TEST_POLICY_ID_1 },
{ artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 },
{ artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_2 },
{ artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_2 },
],
schemaVersion: 'v1',
semanticVersion: '1.0.0',
created: expect.anything(),
},
{ id: 'endpoint-manifest-v1' }
);
});
test('Updates existing saved object if has saved object version', async () => {
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);
const manifest = new Manifest({ soVersion: '1.0.0' });
manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS);
manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS, TEST_POLICY_ID_1);
manifest.addEntry(ARTIFACT_EXCEPTIONS_WINDOWS, TEST_POLICY_ID_2);
manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_1);
manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_2);
context.savedObjectsClient.update = jest
.fn()
.mockImplementation((_type: string, _id: string, object: InternalManifestSchema) => object);
await expect(manifestManager.commit(manifest)).resolves.toBeUndefined();
expect(context.savedObjectsClient.update).toHaveBeenCalledTimes(1);
expect(context.savedObjectsClient.update).toHaveBeenNthCalledWith(
1,
ManifestConstants.SAVED_OBJECT_TYPE,
'endpoint-manifest-v1',
{
artifacts: [
{ artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined },
{ artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: TEST_POLICY_ID_1 },
{ artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 },
{ artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_2 },
{ artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_2 },
],
schemaVersion: 'v1',
semanticVersion: '1.0.0',
},
{ version: '1.0.0' }
);
});
test('Throws error when saved objects client fails', async () => {
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);
const manifest = new Manifest({ soVersion: '1.0.0' });
const error = new Error();
context.savedObjectsClient.update = jest.fn().mockRejectedValue(error);
await expect(manifestManager.commit(manifest)).rejects.toBe(error);
expect(context.savedObjectsClient.update).toHaveBeenCalledTimes(1);
expect(context.savedObjectsClient.update).toHaveBeenNthCalledWith(
1,
ManifestConstants.SAVED_OBJECT_TYPE,
'endpoint-manifest-v1',
{
artifacts: [],
schemaVersion: 'v1',
semanticVersion: '1.0.0',
},
{ version: '1.0.0' }
);
});
});
describe.each([true, false])('buildNewManifest', (unifiedManifestSO) => {
describe('buildNewManifest', () => {
const SUPPORTED_ARTIFACT_NAMES = [
ARTIFACT_NAME_EXCEPTIONS_MACOS,
ARTIFACT_NAME_EXCEPTIONS_WINDOWS,
@ -659,10 +378,8 @@ describe('ManifestManager', () => {
...new Set(artifacts.map((artifact) => artifact.identifier)).values(),
];
test(`Fails when exception list client fails when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => {
const context = buildManifestManagerContextMock({
...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}),
});
test(`Fails when exception list client fails`, async () => {
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);
context.exceptionListClient.findExceptionListItem = jest.fn().mockRejectedValue(new Error());
@ -670,10 +387,8 @@ describe('ManifestManager', () => {
await expect(manifestManager.buildNewManifest()).rejects.toThrow();
});
test(`Builds fully new manifest if no baseline parameter passed and no exception list items when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => {
const context = buildManifestManagerContextMock({
...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}),
});
test(`Builds fully new manifest if no baseline parameter passed and no exception list items`, async () => {
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);
context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({});
@ -701,7 +416,7 @@ describe('ManifestManager', () => {
}
});
test(`Builds fully new manifest if no baseline parameter passed and present exception list items when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => {
test(`Builds fully new manifest if no baseline parameter passed and present exception list items`, async () => {
const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] });
const trustedAppListItem = getExceptionListItemSchemaMock({
os_types: ['linux'],
@ -719,9 +434,7 @@ describe('ManifestManager', () => {
os_types: ['macos'],
tags: ['policy:all'],
});
const context = buildManifestManagerContextMock({
...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}),
});
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);
context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({
@ -783,7 +496,7 @@ describe('ManifestManager', () => {
}
});
test(`Reuses artifacts when baseline parameter passed and present exception list items when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => {
test(`Reuses artifacts when baseline parameter passed and present exception list items`, async () => {
const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] });
const trustedAppListItem = getExceptionListItemSchemaMock({
os_types: ['linux'],
@ -801,9 +514,7 @@ describe('ManifestManager', () => {
os_types: ['macos'],
tags: ['policy:all'],
});
const context = buildManifestManagerContextMock({
...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}),
});
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);
context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({
@ -868,7 +579,7 @@ describe('ManifestManager', () => {
}
});
test(`Builds fully new manifest with single entries when they are duplicated when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => {
test(`Builds fully new manifest with single entries when they are duplicated`, async () => {
const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] });
const trustedAppListItem = getExceptionListItemSchemaMock({
os_types: ['linux'],
@ -886,9 +597,7 @@ describe('ManifestManager', () => {
os_types: ['macos'],
tags: ['policy:all'],
});
const context = buildManifestManagerContextMock({
...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}),
});
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);
const duplicatedEventFilterInDifferentPolicy = {
@ -1002,7 +711,7 @@ describe('ManifestManager', () => {
}
});
test(`Builds manifest with policy specific exception list items for trusted apps when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => {
test(`Builds manifest with policy specific exception list items for trusted apps`, async () => {
const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] });
const trustedAppListItem = getExceptionListItemSchemaMock({
os_types: ['linux'],
@ -1015,9 +724,7 @@ describe('ManifestManager', () => {
],
tags: [`policy:${TEST_POLICY_ID_2}`],
});
const context = buildManifestManagerContextMock({
...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}),
});
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);
context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({
@ -1076,7 +783,7 @@ describe('ManifestManager', () => {
});
});
describe.each([true, false])('buildNewManifest when using app features', (unifiedManifestSO) => {
describe('buildNewManifest when using app features', () => {
const SUPPORTED_ARTIFACT_NAMES = [
ARTIFACT_NAME_EXCEPTIONS_MACOS,
ARTIFACT_NAME_EXCEPTIONS_WINDOWS,
@ -1099,7 +806,7 @@ describe('ManifestManager', () => {
...new Set(artifacts.map((artifact) => artifact.identifier)).values(),
];
test(`when it has endpoint artifact management app feature it should not generate host isolation exceptions when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => {
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'],
@ -1117,10 +824,9 @@ describe('ManifestManager', () => {
os_types: ['macos'],
tags: ['policy:all'],
});
const context = buildManifestManagerContextMock(
{ ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}) },
[ProductFeatureSecurityKey.endpointArtifactManagement]
);
const context = buildManifestManagerContextMock({}, [
ProductFeatureSecurityKey.endpointArtifactManagement,
]);
const manifestManager = new ManifestManager(context);
context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({
@ -1180,7 +886,7 @@ describe('ManifestManager', () => {
}
});
test(`when it has endpoint artifact management and response actions app features it should generate all exceptions when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => {
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'],
@ -1198,13 +904,10 @@ describe('ManifestManager', () => {
os_types: ['macos'],
tags: ['policy:all'],
});
const context = buildManifestManagerContextMock(
{ ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}) },
[
ProductFeatureSecurityKey.endpointArtifactManagement,
ProductFeatureSecurityKey.endpointResponseActions,
]
);
const context = buildManifestManagerContextMock({}, [
ProductFeatureSecurityKey.endpointArtifactManagement,
ProductFeatureSecurityKey.endpointResponseActions,
]);
const manifestManager = new ManifestManager(context);
context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({
@ -1266,7 +969,7 @@ describe('ManifestManager', () => {
}
});
test(`when does not have right app features, should not generate any exception when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => {
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'],
@ -1284,10 +987,7 @@ describe('ManifestManager', () => {
os_types: ['macos'],
tags: ['policy:all'],
});
const context = buildManifestManagerContextMock(
{ ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}) },
[]
);
const context = buildManifestManagerContextMock({}, []);
const manifestManager = new ManifestManager(context);
context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({
@ -1340,102 +1040,93 @@ describe('ManifestManager', () => {
});
});
describe.each([true, false])(
'buildNewManifest when Endpoint Exceptions contain `matches`',
(unifiedManifestSO) => {
test(`when contains only \`wildcard\`, \`event.module=endpoint\` is added when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => {
const exceptionListItem = getExceptionListItemSchemaMock({
os_types: ['macos'],
entries: [
{ type: 'wildcard', operator: 'included', field: 'path', value: '*match_me*' },
{ type: 'wildcard', operator: 'excluded', field: 'not_path', value: '*dont_match_me*' },
],
});
const expectedExceptionListItem = getExceptionListItemSchemaMock({
os_types: ['macos'],
entries: [
...exceptionListItem.entries,
{ type: 'match', operator: 'included', field: 'event.module', value: 'endpoint' },
],
});
const context = buildManifestManagerContextMock({
...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}),
});
const manifestManager = new ManifestManager(context);
context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({
[ENDPOINT_LIST_ID]: { macos: [exceptionListItem] },
});
context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([
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(getArtifactObject(artifacts[0])).toStrictEqual({
entries: translateToEndpointExceptions([expectedExceptionListItem], 'v1'),
});
describe('buildNewManifest when Endpoint Exceptions contain `matches`', () => {
test(`when contains only \`wildcard\`, \`event.module=endpoint\` is added `, async () => {
const exceptionListItem = getExceptionListItemSchemaMock({
os_types: ['macos'],
entries: [
{ type: 'wildcard', operator: 'included', field: 'path', value: '*match_me*' },
{ type: 'wildcard', operator: 'excluded', field: 'not_path', value: '*dont_match_me*' },
],
});
const expectedExceptionListItem = getExceptionListItemSchemaMock({
os_types: ['macos'],
entries: [
...exceptionListItem.entries,
{ type: 'match', operator: 'included', field: 'event.module', value: 'endpoint' },
],
});
test(`when contains anything next to \`wildcard\`, nothing is added when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => {
const exceptionListItem = getExceptionListItemSchemaMock({
os_types: ['macos'],
entries: [
{ type: 'wildcard', operator: 'included', field: 'path', value: '*match_me*' },
{ type: 'wildcard', operator: 'excluded', field: 'path', value: '*dont_match_me*' },
{ type: 'match', operator: 'included', field: 'path', value: 'something' },
],
});
const expectedExceptionListItem = getExceptionListItemSchemaMock({
os_types: ['macos'],
entries: [...exceptionListItem.entries],
});
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);
const context = buildManifestManagerContextMock({
...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}),
});
const manifestManager = new ManifestManager(context);
context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({
[ENDPOINT_LIST_ID]: { macos: [exceptionListItem] },
});
context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([
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(getArtifactObject(artifacts[0])).toStrictEqual({
entries: translateToEndpointExceptions([expectedExceptionListItem], 'v1'),
});
context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({
[ENDPOINT_LIST_ID]: { macos: [exceptionListItem] },
});
}
);
describe.each([true, false])('deleteArtifacts', (unifiedManifestSO) => {
test(`Successfully invokes saved objects client when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => {
const context = buildManifestManagerContextMock({
...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}),
context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([
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(getArtifactObject(artifacts[0])).toStrictEqual({
entries: translateToEndpointExceptions([expectedExceptionListItem], 'v1'),
});
});
test(`when contains anything next to \`wildcard\`, nothing is added `, async () => {
const exceptionListItem = getExceptionListItemSchemaMock({
os_types: ['macos'],
entries: [
{ type: 'wildcard', operator: 'included', field: 'path', value: '*match_me*' },
{ type: 'wildcard', operator: 'excluded', field: 'path', value: '*dont_match_me*' },
{ type: 'match', operator: 'included', field: 'path', value: 'something' },
],
});
const expectedExceptionListItem = getExceptionListItemSchemaMock({
os_types: ['macos'],
entries: [...exceptionListItem.entries],
});
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);
context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({
[ENDPOINT_LIST_ID]: { macos: [exceptionListItem] },
});
context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([
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(getArtifactObject(artifacts[0])).toStrictEqual({
entries: translateToEndpointExceptions([expectedExceptionListItem], 'v1'),
});
});
});
describe('deleteArtifacts', () => {
test(`Successfully invokes saved objects client`, async () => {
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);
await expect(
@ -1451,10 +1142,8 @@ describe('ManifestManager', () => {
]);
});
test(`Returns errors for partial failures when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => {
const context = buildManifestManagerContextMock({
...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}),
});
test(`Returns errors for partial failures`, async () => {
const context = buildManifestManagerContextMock({});
const artifactClient = context.artifactClient as jest.Mocked<EndpointArtifactClientInterface>;
const manifestManager = new ManifestManager(context);
const error = new Error();
@ -1481,11 +1170,9 @@ describe('ManifestManager', () => {
});
});
describe.each([true, false])('pushArtifacts', (unifiedManifestSO) => {
test(`Successfully invokes artifactClient when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => {
const context = buildManifestManagerContextMock({
...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}),
});
describe('pushArtifacts', () => {
test(`Successfully invokes artifactClient `, async () => {
const context = buildManifestManagerContextMock({});
const artifactClient = context.artifactClient as jest.Mocked<EndpointArtifactClientInterface>;
const manifestManager = new ManifestManager(context);
const newManifest = ManifestManager.createDefaultManifest();
@ -1507,10 +1194,8 @@ describe('ManifestManager', () => {
]);
});
test(`Returns errors for partial failures when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => {
const context = buildManifestManagerContextMock({
...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}),
});
test(`Returns errors for partial failures`, async () => {
const context = buildManifestManagerContextMock({});
const artifactClient = context.artifactClient as jest.Mocked<EndpointArtifactClientInterface>;
const manifestManager = new ManifestManager(context);
const newManifest = ManifestManager.createDefaultManifest();
@ -1551,16 +1236,14 @@ describe('ManifestManager', () => {
});
});
describe.each([true, false])('tryDispatch', (unifiedSavedObject) => {
describe('tryDispatch', () => {
const getMockPolicyFetchAllItems = (items: PackagePolicy[]) =>
jest.fn(async function* () {
yield items;
});
test(`Should not dispatch if no policies when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => {
const context = buildManifestManagerContextMock({
...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}),
});
test(`Should not dispatch if no policies`, async () => {
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);
const manifest = new Manifest({ soVersion: '1.0.0' });
@ -1572,10 +1255,8 @@ describe('ManifestManager', () => {
expect(context.packagePolicyService.bulkUpdate).toHaveBeenCalledTimes(0);
});
test(`Should return errors if invalid config for package policy when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => {
const context = buildManifestManagerContextMock({
...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}),
});
test(`Should return errors if invalid config for package policy`, async () => {
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);
const manifest = new Manifest({ soVersion: '1.0.0' });
@ -1592,10 +1273,8 @@ describe('ManifestManager', () => {
expect(context.packagePolicyService.bulkUpdate).toHaveBeenCalledTimes(0);
});
test(`Should not dispatch if semantic version has not changed when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => {
const context = buildManifestManagerContextMock({
...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}),
});
test(`Should not dispatch if semantic version has not changed`, async () => {
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);
const manifest = new Manifest({ soVersion: '1.0.0' });
@ -1623,10 +1302,8 @@ describe('ManifestManager', () => {
expect(context.packagePolicyService.bulkUpdate).toHaveBeenCalledTimes(0);
});
test(`Should dispatch to only policies where list of artifacts changed when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => {
const context = buildManifestManagerContextMock({
...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}),
});
test(`Should dispatch to only policies where list of artifacts changed`, async () => {
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);
const manifest = new Manifest({ soVersion: '1.0.0', semanticVersion: '1.0.1' });
@ -1694,10 +1371,8 @@ describe('ManifestManager', () => {
);
});
test(`Should dispatch to only policies where artifact content changed when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => {
const context = buildManifestManagerContextMock({
...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}),
});
test(`Should dispatch to only policies where artifact content changed`, async () => {
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);
const manifest = new Manifest({ soVersion: '1.0.0', semanticVersion: '1.0.1' });
@ -1767,10 +1442,8 @@ describe('ManifestManager', () => {
);
});
test(`Should return partial errors when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => {
const context = buildManifestManagerContextMock({
...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}),
});
test(`Should return partial errors`, async () => {
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);
const error = new Error();
@ -1813,11 +1486,9 @@ describe('ManifestManager', () => {
});
});
describe.each([true, false])('cleanup artifacts', (unifiedSavedObject) => {
test(`Successfully removes orphan artifacts when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => {
const context = buildManifestManagerContextMock({
...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}),
});
describe('cleanup artifacts', () => {
test(`Successfully removes orphan artifacts`, async () => {
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);
(context.artifactClient.fetchAll as jest.Mock).mockReturnValue(
@ -1845,10 +1516,8 @@ describe('ManifestManager', () => {
]);
});
test(`When there is no artifact to be removed when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => {
const context = buildManifestManagerContextMock({
...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}),
});
test(`When there is no artifact to be removed`, async () => {
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);
context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({});

View file

@ -29,7 +29,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
const pageObjects = getPageObjects(['common', 'artifactEntriesList']);
const testSubjects = getService('testSubjects');
const browser = getService('browser');
const endpointArtifactTestResources = getService('endpointArtifactTestResources');
const endpointArtifactsTestResources = getService('endpointArtifactTestResources');
const endpointTestResources = getService('endpointTestResources');
const retry = getService('retry');
const esClient = getService('es');
@ -51,9 +51,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
.delete(`${EXCEPTION_LIST_URL}?list_id=${listId}&namespace_type=agnostic`)
.set('kbn-xsrf', 'true');
};
// It's flaky only in Serverless
// Failing: See https://github.com/elastic/kibana/issues/186004
describe.skip('@ess @serverless For each artifact list under management', function () {
describe('@ess @serverless For each artifact list under management', function () {
let indexedData: IndexedHostsAndAlertsResponse;
let policyInfo: PolicyTestResourceInfo;
@ -73,13 +72,14 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
// Check edited artifact is in the list with new values (wait for list to be updated)
let updatedArtifact: ArtifactElasticsearchProperties | undefined;
await retry.waitForWithTimeout('fleet artifact is updated', 120_000, async () => {
const artifacts = await endpointArtifactTestResources.getArtifacts();
const artifacts = await endpointArtifactsTestResources.getArtifactsFromUnifiedManifestSO();
// This expects manifest artifact to come from unified so
const manifestArtifact = artifacts.find((artifact) => {
return (
artifact.artifactId ===
`${expectedArtifact.identifier}-${expectedArtifact.decoded_sha256}` &&
artifact.policyId === policy?.packagePolicy.id
artifact.artifactIds.includes(
`${expectedArtifact.identifier}-${expectedArtifact.decoded_sha256}`
) && artifact.policyId === policy?.packagePolicy.id
);
});

View file

@ -99,18 +99,18 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
const checkArtifact = (expectedArtifact: object) => {
return retry.tryForTime(2 * MINUTES, async () => {
const artifacts = await endpointArtifactTestResources.getArtifacts();
const artifacts = await endpointArtifactTestResources.getArtifactsFromUnifiedManifestSO();
const manifestArtifact = artifacts.find((artifact) =>
artifact.artifactId.startsWith('endpoint-exceptionlist-macos-v1')
);
const foundArtifactId = artifacts
.flatMap((artifact) => artifact.artifactIds)
.find((artifactId) => artifactId.startsWith('endpoint-exceptionlist-macos-v1'));
expect(manifestArtifact).to.not.be(undefined);
expect(foundArtifactId).to.not.be(undefined);
// Get fleet artifact
const artifactResult = await esClient.get({
index: '.fleet-artifacts-7',
id: `endpoint:${manifestArtifact!.artifactId}`,
id: `endpoint:${foundArtifactId!}`,
});
const artifact = artifactResult._source as ArtifactElasticsearchProperties;

View file

@ -7,7 +7,7 @@
import { FullAgentPolicy } from '@kbn/fleet-plugin/common/types';
import { ArtifactElasticsearchProperties } from '@kbn/fleet-plugin/server/services/artifacts/types';
import { InternalManifestSchema } from '@kbn/security-solution-plugin/server/endpoint/schemas/artifacts';
import { InternalUnifiedManifestBaseSchema } from '@kbn/security-solution-plugin/server/endpoint/schemas/artifacts';
import { TranslatedExceptionListItem } from '@kbn/security-solution-plugin/server/endpoint/schemas/artifacts/lists';
export interface AgentPolicyResponseType {
@ -17,12 +17,12 @@ export interface AgentPolicyResponseType {
_source: { data: FullAgentPolicy };
}
export interface InternalManifestSchemaResponseType {
export interface InternalUnifiedManifestSchemaResponseType {
_index: string;
_id: string;
_score: number;
_source: {
'endpoint:user-artifact-manifest': InternalManifestSchema;
'endpoint:unified-user-artifact-manifest': InternalUnifiedManifestBaseSchema;
};
}

View file

@ -1,373 +0,0 @@
/*
* 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 { unzip } from 'zlib';
import { promisify } from 'util';
import expect from '@kbn/expect';
import { IndexedHostsAndAlertsResponse } from '@kbn/security-solution-plugin/common/endpoint/index_data';
import {
ENDPOINT_ARTIFACT_LIST_IDS,
EXCEPTION_LIST_URL,
} from '@kbn/securitysolution-list-constants';
import { ArtifactElasticsearchProperties } from '@kbn/fleet-plugin/server/services';
import { FtrProviderContext } from '../../configs/ftr_provider_context';
import {
ArtifactBodyType,
getArtifactsListTestsData,
ArtifactActionsType,
AgentPolicyResponseType,
getCreateMultipleData,
MultipleArtifactActionsType,
} from './mocks';
import { PolicyTestResourceInfo } from '../../services/endpoint_policy';
export default ({ getPageObjects, getService }: FtrProviderContext) => {
const pageObjects = getPageObjects(['common', 'artifactEntriesList']);
const testSubjects = getService('testSubjects');
const browser = getService('browser');
const endpointArtifactsTestResources = getService('endpointArtifactTestResources');
const endpointTestResources = getService('endpointTestResources');
const retry = getService('retry');
const esClient = getService('es');
const supertest = getService('supertest');
const find = getService('find');
const toasts = getService('toasts');
const policyTestResources = getService('policyTestResources');
const unzipPromisify = promisify(unzip);
const removeAllArtifacts = async () => {
for (const listId of ENDPOINT_ARTIFACT_LIST_IDS) {
await removeExceptionsList(listId);
}
};
const removeExceptionsList = async (listId: string) => {
await supertest
.delete(`${EXCEPTION_LIST_URL}?list_id=${listId}&namespace_type=agnostic`)
.set('kbn-xsrf', 'true');
};
// Failing: See https://github.com/elastic/kibana/issues/183860
describe.skip('@ess @serverless For each artifact list under management', function () {
let indexedData: IndexedHostsAndAlertsResponse;
let policyInfo: PolicyTestResourceInfo;
before(async () => {
indexedData = await endpointTestResources.loadEndpointData();
});
after(async () => {
await endpointTestResources.unloadEndpointData(indexedData);
});
const checkFleetArtifacts = async (
identifier: string,
expectedArtifact: ArtifactElasticsearchProperties,
expectedDecodedBodyArtifact: ArtifactBodyType,
policy?: PolicyTestResourceInfo
) => {
// Check edited artifact is in the list with new values (wait for list to be updated)
let updatedArtifact: ArtifactElasticsearchProperties | undefined;
await retry.waitForWithTimeout('fleet artifact is updated', 120_000, async () => {
const artifacts = await endpointArtifactsTestResources.getArtifactsFromUnifiedManifestSO();
// This expects manifest artifact to come from unified so
const manifestArtifact = artifacts.find((artifact) => {
return (
artifact.artifactIds.includes(
`${expectedArtifact.identifier}-${expectedArtifact.decoded_sha256}`
) && artifact.policyId === policy?.packagePolicy.id
);
});
if (!manifestArtifact) return false;
// Get fleet artifact
const windowsArtifactResult = await esClient.get({
index: '.fleet-artifacts-7',
id: `endpoint:${expectedArtifact.identifier}-${expectedArtifact.decoded_sha256}`,
});
const windowsArtifact = windowsArtifactResult._source as ArtifactElasticsearchProperties;
// Get agent policy
const {
hits: { hits: policiesResults },
} = await esClient.search({
index: '.fleet-policies*',
query: {
bool: {
filter: [
{
match: {
policy_id: policy?.agentPolicy.id,
},
},
],
},
},
sort: [{ revision_idx: { order: 'desc' } }],
size: 1,
});
const agentPolicyResults = policiesResults[0] as AgentPolicyResponseType;
const policyArtifactManifest = agentPolicyResults._source.data.inputs[0]
? agentPolicyResults._source.data.inputs[0].artifact_manifest
: undefined;
let isUpdated: boolean = false;
if (policyArtifactManifest) {
// Compare artifacts from fleet artifacts and agent policy are the expecteds
isUpdated =
windowsArtifact.encoded_sha256 === expectedArtifact.encoded_sha256 &&
policyArtifactManifest.artifacts[identifier].encoded_sha256 ===
expectedArtifact.encoded_sha256;
}
if (isUpdated) updatedArtifact = windowsArtifact;
return isUpdated;
});
updatedArtifact!.created = expectedArtifact.created;
const bodyFormBuffer = Buffer.from(updatedArtifact!.body, 'base64');
const unzippedBody = await unzipPromisify(bodyFormBuffer);
// Check decoded body first to detect possible body changes
expect(JSON.parse(unzippedBody.toString())).eql(expectedDecodedBodyArtifact);
expect(updatedArtifact).eql(expectedArtifact);
};
const performActions = async (
actions:
| ArtifactActionsType['create']['formFields']
| ArtifactActionsType['update']['formFields'],
suffix?: string
) => {
for (const formAction of actions) {
if (formAction.type === 'customClick') {
await find.clickByCssSelector(formAction.selector, testSubjects.FIND_TIME);
} else if (formAction.type === 'click') {
await testSubjects.click(formAction.selector);
} else if (formAction.type === 'input') {
await testSubjects.setValue(
formAction.selector,
(formAction.value || '') + (suffix ? suffix : '')
);
} else if (formAction.type === 'clear') {
await (
await (await testSubjects.find(formAction.selector)).findByCssSelector('button')
).click();
}
}
};
const deleteArtifact = async (actions: ArtifactActionsType) => {
await pageObjects.artifactEntriesList.clickCardActionMenu(actions.pagePrefix);
await testSubjects.click(`${actions.pagePrefix}-card-cardDeleteAction`);
await testSubjects.click(`${actions.pagePrefix}-deleteModal-submitButton`);
await testSubjects.waitForDeleted(actions.delete.confirmSelector);
};
const createArtifact = async (
actions: ArtifactActionsType | MultipleArtifactActionsType,
options?: { policyId?: string; suffix?: string; createButton?: string }
) => {
// Opens add flyout
if (options?.createButton) {
await testSubjects.click(`${actions.pagePrefix}-${options.createButton}`);
} else {
await testSubjects.click(`${actions.pagePrefix}-emptyState-addButton`);
}
await performActions(actions.create.formFields, options?.suffix);
if (options?.policyId) {
await testSubjects.click(`${actions.pageObject}-form-effectedPolicies-perPolicy`);
await testSubjects.click(`policy-${options.policyId}-checkbox`);
}
// Submit create artifact form
await testSubjects.click(`${actions.pagePrefix}-flyout-submitButton`);
};
const updateArtifact = async (
actions: ArtifactActionsType,
options?: { policyId?: string; suffix?: string }
) => {
// Opens edit flyout
await pageObjects.artifactEntriesList.clickCardActionMenu(actions.pagePrefix);
await testSubjects.click(`${actions.pagePrefix}-card-cardEditAction`);
await performActions(actions.update.formFields);
if (options?.policyId) {
await testSubjects.click(`${actions.pageObject}-form-effectedPolicies-perPolicy`);
await testSubjects.click(`policy-${options.policyId}-checkbox`);
}
// Submit edit artifact form
await testSubjects.click(`${actions.pagePrefix}-flyout-submitButton`);
};
for (const testData of getArtifactsListTestsData()) {
describe(`When on the ${testData.title} entries list`, function () {
beforeEach(async () => {
policyInfo = await policyTestResources.createPolicy();
await removeAllArtifacts();
await browser.refresh();
await pageObjects.artifactEntriesList.navigateToList(testData.urlPath);
});
afterEach(async () => {
await removeAllArtifacts();
if (policyInfo) {
await policyInfo.cleanup();
}
});
it(`should not show page title if there is no ${testData.title} entry`, async () => {
await testSubjects.missingOrFail('header-page-title');
});
it(`should be able to add a new ${testData.title} entry`, async () => {
await createArtifact(testData, { policyId: policyInfo.packagePolicy.id });
// Check new artifact is in the list
for (const checkResult of testData.create.checkResults) {
expect(await testSubjects.getVisibleText(checkResult.selector)).to.equal(
checkResult.value
);
}
await toasts.dismiss();
// Title is shown after adding an item
expect(await testSubjects.getVisibleText('header-page-title')).to.equal(testData.title);
// Checks if fleet artifact has been updated correctly
await checkFleetArtifacts(
testData.fleetArtifact.identifier,
testData.fleetArtifact.getExpectedUpdatedtArtifactWhenCreate(),
testData.fleetArtifact.getExpectedUpdatedArtifactBodyWhenCreate(),
policyInfo
);
});
it(`should be able to update an existing ${testData.title} entry`, async () => {
await createArtifact(testData);
await updateArtifact(testData, { policyId: policyInfo.packagePolicy.id });
// Check edited artifact is in the list with new values (wait for list to be updated)
await retry.waitForWithTimeout('entry is updated in list', 20000, async () => {
const currentValue = await testSubjects.getVisibleText(
`${testData.pagePrefix}-card-criteriaConditions${
testData.pagePrefix === 'EventFiltersListPage' ? '-condition' : ''
}`
);
return currentValue === testData.update.waitForValue;
});
for (const checkResult of testData.update.checkResults) {
expect(await testSubjects.getVisibleText(checkResult.selector)).to.equal(
checkResult.value
);
}
await toasts.dismiss();
// Title still shown after editing an item
expect(await testSubjects.getVisibleText('header-page-title')).to.equal(testData.title);
// Checks if fleet artifact has been updated correctly
await checkFleetArtifacts(
testData.fleetArtifact.identifier,
testData.fleetArtifact.getExpectedUpdatedArtifactWhenUpdate(),
testData.fleetArtifact.getExpectedUpdatedArtifactBodyWhenUpdate(),
policyInfo
);
});
it(`should be able to delete the existing ${testData.title} entry`, async () => {
await createArtifact(testData);
await deleteArtifact(testData);
// We only expect one artifact to have been visible
await testSubjects.missingOrFail(testData.delete.card);
// Header has gone because there is no artifact
await testSubjects.missingOrFail('header-page-title');
});
});
}
describe('Should check artifacts are correctly generated when multiple entries', function () {
let firstPolicy: PolicyTestResourceInfo;
let secondPolicy: PolicyTestResourceInfo;
const firstSuffix = 'first';
const secondSuffix = 'second';
const thirdSuffix = 'third';
beforeEach(async () => {
firstPolicy = await policyTestResources.createPolicy();
secondPolicy = await policyTestResources.createPolicy();
await removeAllArtifacts();
await browser.refresh();
await pageObjects.artifactEntriesList.navigateToList(testData.urlPath);
});
afterEach(async () => {
await removeAllArtifacts();
if (firstPolicy) {
await firstPolicy.cleanup();
}
if (secondPolicy) {
await secondPolicy.cleanup();
}
});
const testData = getCreateMultipleData();
it(`should get correct atifact when multiple entries are created`, async () => {
// Create first trusted app
await createArtifact(testData, {
policyId: firstPolicy.packagePolicy.id,
suffix: firstSuffix,
});
await toasts.dismiss();
// Create second trusted app
await createArtifact(testData, {
policyId: secondPolicy.packagePolicy.id,
suffix: secondSuffix,
createButton: 'pageAddButton',
});
await toasts.dismiss();
// Create third trusted app
await createArtifact(testData, { suffix: thirdSuffix, createButton: 'pageAddButton' });
await toasts.dismiss();
// Checks if fleet artifact has been updated correctly
await checkFleetArtifacts(
testData.fleetArtifact.identifier,
testData.fleetArtifact.getExpectedUpdatedArtifactWhenCreateMultipleFirst(),
testData.fleetArtifact.getExpectedUpdatedArtifactBodyWhenCreateMultipleFirst(
thirdSuffix,
firstSuffix
),
firstPolicy
);
// Checks if fleet artifact has been updated correctly
await checkFleetArtifacts(
testData.fleetArtifact.identifier,
testData.fleetArtifact.getExpectedUpdatedArtifactWhenCreateMultipleSecond(),
testData.fleetArtifact.getExpectedUpdatedArtifactBodyWhenCreateMultipleSecond(
thirdSuffix,
secondSuffix
),
secondPolicy
);
});
});
});
};

View file

@ -1,250 +0,0 @@
/*
* 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 { unzip } from 'zlib';
import { promisify } from 'util';
import expect from '@kbn/expect';
import { IndexedHostsAndAlertsResponse } from '@kbn/security-solution-plugin/common/endpoint/index_data';
import { EXCEPTION_LIST_ITEM_URL } from '@kbn/securitysolution-list-constants';
import { ArtifactElasticsearchProperties } from '@kbn/fleet-plugin/server/services';
import { FoundExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
import { WebElementWrapper } from '@kbn/ftr-common-functional-ui-services';
import { FtrProviderContext } from '../../configs/ftr_provider_context';
export default ({ getPageObjects, getService }: FtrProviderContext) => {
const pageObjects = getPageObjects(['common', 'header']);
const queryBar = getService('queryBar');
const testSubjects = getService('testSubjects');
const endpointTestResources = getService('endpointTestResources');
const endpointArtifactTestResources = getService('endpointArtifactTestResources');
const retry = getService('retry');
const esClient = getService('es');
const supertest = getService('supertest');
const find = getService('find');
const unzipPromisify = promisify(unzip);
const comboBox = getService('comboBox');
const toasts = getService('toasts');
const timeout = 60000 * 10;
// Failing: See https://github.com/elastic/kibana/issues/184585
describe.skip('@ess @serverless Endpoint Exceptions', function () {
const clearPrefilledEntries = async () => {
const entriesContainer = await testSubjects.find('exceptionEntriesContainer');
let deleteButtons: WebElementWrapper[];
do {
deleteButtons = await testSubjects.findAllDescendant(
'builderItemEntryDeleteButton',
entriesContainer
);
await deleteButtons[0].click();
} while (deleteButtons.length > 1);
};
const openNewEndpointExceptionFlyout = async () => {
await testSubjects.click('timeline-context-menu-button');
await testSubjects.click('add-endpoint-exception-menu-item');
await testSubjects.existOrFail('addExceptionFlyout');
await retry.waitFor('entries should be loaded', () =>
testSubjects.exists('exceptionItemEntryContainer')
);
};
const setLastFieldsValue = async ({
testSubj,
value,
}: {
testSubj: string;
value: string;
optionSelector?: string;
}) => {
const fields = await find.allByCssSelector(`[data-test-subj="${testSubj}"]`);
const lastField = fields[fields.length - 1];
await lastField.click();
await retry.try(
async () => {
await comboBox.setElement(lastField, value);
},
async () => {
// If the above fails due to an option not existing, create the value custom instead
await comboBox.setFilterValue(lastField, value);
await pageObjects.common.pressEnterKey();
}
);
};
const setLastEntry = async ({
field,
operator,
value,
}: {
field: string;
operator: 'matches' | 'is';
value: string;
}) => {
await setLastFieldsValue({ testSubj: 'fieldAutocompleteComboBox', value: field });
await setLastFieldsValue({ testSubj: 'operatorAutocompleteComboBox', value: operator });
await setLastFieldsValue({
testSubj: operator === 'matches' ? 'valuesAutocompleteWildcard' : 'valuesAutocompleteMatch',
value,
});
};
const checkArtifact = (expectedArtifact: object) => {
return retry.tryForTime(120_000, async () => {
const artifacts = await endpointArtifactTestResources.getArtifactsFromUnifiedManifestSO();
const foundArtifactId = artifacts
.flatMap((artifact) => artifact.artifactIds)
.find((artifactId) => artifactId.startsWith('endpoint-exceptionlist-macos-v1'));
expect(foundArtifactId).to.not.be(undefined);
// Get fleet artifact
const artifactResult = await esClient.get({
index: '.fleet-artifacts-7',
id: `endpoint:${foundArtifactId!}`,
});
const artifact = artifactResult._source as ArtifactElasticsearchProperties;
const zippedBody = Buffer.from(artifact.body, 'base64');
const artifactBody = await unzipPromisify(zippedBody);
expect(JSON.parse(artifactBody.toString())).to.eql(expectedArtifact);
});
};
let indexedData: IndexedHostsAndAlertsResponse;
before(async () => {
indexedData = await endpointTestResources.loadEndpointData();
const waitForAlertsToAppear = async () => {
await pageObjects.common.navigateToUrlWithBrowserHistory('security', `/alerts`);
await pageObjects.header.waitUntilLoadingHasFinished();
await retry.waitForWithTimeout('alerts to appear', 10 * 60_000, async () => {
await queryBar.clickQuerySubmitButton();
return testSubjects.exists('timeline-context-menu-button');
});
};
await waitForAlertsToAppear();
});
after(async () => {
await endpointTestResources.unloadEndpointData(indexedData);
});
beforeEach(async () => {
const deleteEndpointExceptions = async () => {
const { body } = await supertest
.get(`${EXCEPTION_LIST_ITEM_URL}/_find?list_id=endpoint_list&namespace_type=agnostic`)
.set('kbn-xsrf', 'true');
for (const exceptionListItem of (body as FoundExceptionListItemSchema).data) {
await supertest
.delete(`${EXCEPTION_LIST_ITEM_URL}?id=${exceptionListItem.id}&namespace_type=agnostic`)
.set('kbn-xsrf', 'true');
}
};
await deleteEndpointExceptions();
}, timeout);
it(
'should add `event.module=endpoint` to entry if only wildcard operator is present',
async () => {
await pageObjects.common.navigateToUrlWithBrowserHistory('security', `/alerts`);
await openNewEndpointExceptionFlyout();
await clearPrefilledEntries();
await testSubjects.setValue('exceptionFlyoutNameInput', 'test exception');
await setLastEntry({ field: 'file.path', operator: 'matches', value: '*/cheese/*' });
await testSubjects.click('exceptionsAndButton');
await setLastEntry({ field: 'process.executable', operator: 'matches', value: 'ex*' });
await testSubjects.click('addExceptionConfirmButton');
await toasts.dismiss();
await checkArtifact({
entries: [
{
type: 'simple',
entries: [
{
field: 'file.path',
operator: 'included',
type: 'wildcard_cased',
value: '*/cheese/*',
},
{
field: 'process.executable',
operator: 'included',
type: 'wildcard_cased',
value: 'ex*',
},
{
// this additional entry should be added
field: 'event.module',
operator: 'included',
type: 'exact_cased',
value: 'endpoint',
},
],
},
],
});
},
timeout
);
it(
'should NOT add `event.module=endpoint` to entry if there is another operator',
async () => {
await pageObjects.common.navigateToUrlWithBrowserHistory('security', `/alerts`);
await openNewEndpointExceptionFlyout();
await clearPrefilledEntries();
await testSubjects.setValue('exceptionFlyoutNameInput', 'test exception');
await setLastEntry({ field: 'file.path', operator: 'matches', value: '*/cheese/*' });
await testSubjects.click('exceptionsAndButton');
await setLastEntry({ field: 'process.executable', operator: 'is', value: 'something' });
await testSubjects.click('addExceptionConfirmButton');
await toasts.dismiss();
await checkArtifact({
entries: [
{
type: 'simple',
entries: [
{
field: 'file.path',
operator: 'included',
type: 'wildcard_cased',
value: '*/cheese/*',
},
{
field: 'process.executable',
operator: 'included',
type: 'exact_cased',
value: 'something',
},
],
},
],
});
},
timeout
);
});
};

View file

@ -1,48 +0,0 @@
/*
* 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 { getRegistryUrl as getRegistryUrlFromIngest } from '@kbn/fleet-plugin/server';
import { isServerlessKibanaFlavor } from '@kbn/security-solution-plugin/scripts/endpoint/common/stack_services';
import { FtrProviderContext } from '../../configs/ftr_provider_context';
import {
getRegistryUrlFromTestEnv,
isRegistryEnabled,
} from '../../../security_solution_endpoint_api_int/registry';
export default function (providerContext: FtrProviderContext) {
const { loadTestFile, getService, getPageObjects } = providerContext;
describe('endpoint', function () {
const ingestManager = getService('ingestManager');
const log = getService('log');
const endpointTestResources = getService('endpointTestResources');
const kbnClient = getService('kibanaServer');
if (!isRegistryEnabled()) {
log.warning('These tests are being run with an external package registry');
}
const registryUrl = getRegistryUrlFromTestEnv() ?? getRegistryUrlFromIngest();
log.info(`Package registry URL for tests: ${registryUrl}`);
before(async () => {
log.info('calling Fleet setup');
await ingestManager.setup();
log.info('installing/upgrading Endpoint fleet package');
await endpointTestResources.installOrUpgradeEndpointFleetPackage();
if (await isServerlessKibanaFlavor(kbnClient)) {
log.info('login for serverless environment');
const pageObjects = getPageObjects(['svlCommonPage']);
await pageObjects.svlCommonPage.login();
}
});
loadTestFile(require.resolve('./artifact_entries_list'));
loadTestFile(require.resolve('./endpoint_exceptions'));
});
}

View file

@ -1,807 +0,0 @@
/*
* 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 { FullAgentPolicy } from '@kbn/fleet-plugin/common/types';
import { ArtifactElasticsearchProperties } from '@kbn/fleet-plugin/server/services/artifacts/types';
import { InternalUnifiedManifestBaseSchema } from '@kbn/security-solution-plugin/server/endpoint/schemas/artifacts';
import { TranslatedExceptionListItem } from '@kbn/security-solution-plugin/server/endpoint/schemas/artifacts/lists';
export interface AgentPolicyResponseType {
_index: string;
_id: string;
_score: number;
_source: { data: FullAgentPolicy };
}
export interface InternalUnifiedManifestSchemaResponseType {
_index: string;
_id: string;
_score: number;
_source: {
'endpoint:unified-user-artifact-manifest': InternalUnifiedManifestBaseSchema;
};
}
export interface ArtifactBodyType {
entries: TranslatedExceptionListItem[];
}
export type ArtifactActionsType = ReturnType<typeof getArtifactsListTestsData>[0];
export type MultipleArtifactActionsType = ReturnType<typeof getCreateMultipleData>;
export const getArtifactsListTestsData = () => [
{
title: 'Trusted applications',
pagePrefix: 'trustedAppsListPage',
create: {
formFields: [
{
type: 'input',
selector: 'trustedApps-form-descriptionField',
value: 'This is the trusted application description',
},
{
type: 'input',
selector: 'trustedApps-form-nameTextField',
value: 'Trusted application name',
},
{
type: 'click',
selector: 'trustedApps-form-conditionsBuilder-group1-entry0-field',
},
{
type: 'click',
selector: 'trustedApps-form-conditionsBuilder-group1-entry0-field-type-Hash',
},
{
type: 'input',
selector: 'trustedApps-form-conditionsBuilder-group1-entry0-value',
value: 'A4370C0CF81686C0B696FA6261c9d3e0d810ae704ab8301839dffd5d5112f476',
},
],
checkResults: [
{
selector: 'trustedAppsListPage-card-criteriaConditions',
value:
'OSIS Windows\nAND process.hash.*IS a4370c0cf81686c0b696fa6261c9d3e0d810ae704ab8301839dffd5d5112f476',
},
],
},
update: {
formFields: [
{
type: 'input',
selector: 'trustedApps-form-descriptionField',
value: 'This is the trusted application description edited',
},
{
type: 'input',
selector: 'trustedApps-form-nameTextField',
value: 'Trusted application name edited',
},
{
type: 'click',
selector: 'trustedApps-form-conditionsBuilder-group1-entry0-field',
},
{
type: 'click',
selector: 'trustedApps-form-conditionsBuilder-group1-entry0-field-type-Path',
},
{
type: 'input',
selector: 'trustedApps-form-conditionsBuilder-group1-entry0-value',
value: 'c:\\randomFolder\\randomFile.exe, c:\\randomFolder\\randomFile2.exe',
},
],
checkResults: [
{
selector: 'trustedAppsListPage-card-criteriaConditions',
value:
'OSIS Windows\nAND process.executable.caselessIS c:\\randomFolder\\randomFile.exe, c:\\randomFolder\\randomFile2.exe',
},
{
selector: 'trustedAppsListPage-card-header-title',
value: 'Trusted application name edited',
},
{
selector: 'trustedAppsListPage-card-description',
value: 'This is the trusted application description edited',
},
],
waitForValue:
'OSIS Windows\nAND process.executable.caselessIS c:\\randomFolder\\randomFile.exe, c:\\randomFolder\\randomFile2.exe',
},
delete: {
confirmSelector: 'trustedAppsListPage-deleteModal-submitButton',
card: 'trustedAppsListPage-card',
},
urlPath: 'trusted_apps',
pageObject: 'trustedApps',
fleetArtifact: {
identifier: 'endpoint-trustlist-windows-v1',
type: 'trustedApplications',
getExpectedUpdatedtArtifactWhenCreate: (): ArtifactElasticsearchProperties => ({
type: 'trustlist',
identifier: 'endpoint-trustlist-windows-v1',
body: 'eJxVzNEKgyAUgOF3OdcxNMvMVxkxTp4jCa5EbWxE7z422MVuvx/+A3itOXABez2gvhKDhRLuKTI0f80HjgQWUt4cl3JZsCyXsmDba2hgS5yxbhkshNXFnZig+f34ia7eHJYvPjDuH8VODcIJ543URjsx61F71K2WbiTFgowUyIPocDZKSKNG8p566qVsfTdoOKdzOt89hz0Q',
package_name: 'endpoint',
created: '2000-01-01T00:00:00.000Z',
relative_url:
'/api/fleet/artifacts/endpoint-trustlist-windows-v1/016bec11c5b1d6f8609fd3525202aa12baf0132484abf368d5011100d5ec1ec4',
compression_algorithm: 'zlib',
decoded_size: 193,
decoded_sha256: '016bec11c5b1d6f8609fd3525202aa12baf0132484abf368d5011100d5ec1ec4',
encryption_algorithm: 'none',
encoded_sha256: '814aabc04d674ccdeb7c1acfe74120cb52ad1392d6924a7d813e08f8b6cd0f0f',
encoded_size: 153,
}),
getExpectedUpdatedArtifactBodyWhenCreate: (): ArtifactBodyType => ({
entries: [
{
type: 'simple',
entries: [
{
field: 'process.hash.sha256',
operator: 'included',
type: 'exact_cased',
value: 'a4370c0cf81686c0b696fa6261c9d3e0d810ae704ab8301839dffd5d5112f476',
},
],
},
],
}),
getExpectedUpdatedArtifactWhenUpdate: (): ArtifactElasticsearchProperties => ({
type: 'trustlist',
identifier: 'endpoint-trustlist-windows-v1',
body: 'eJx9jEEKwjAUBa8ibx1cuMwBvIQtEpMnBH6TkJ9KpeTuEkHBjcthhtnB1Gqkwl52tGchLDQuRQjz4+6REmBRavZUPXKjX5u7vcNcWF3LFRYxeVkDA8xnx835dvVOKVSFwcPJOoS301RdCnk5ZwmsX4rC8TeHf8VpJOhzn/sLJpZG8A==',
package_name: 'endpoint',
created: '2000-01-01T00:00:00.000Z',
relative_url:
'/api/fleet/artifacts/endpoint-trustlist-windows-v1/ac2bf74a73885f9a5a1700c328bf1a5a8f6cb72f2465a575335ea99dac0d4c10',
compression_algorithm: 'zlib',
decoded_size: 198,
decoded_sha256: 'ac2bf74a73885f9a5a1700c328bf1a5a8f6cb72f2465a575335ea99dac0d4c10',
encryption_algorithm: 'none',
encoded_sha256: '28d81b2787cea23fcb88d02b1c09940858963a62c60cdfd7a2b7564cfc251708',
encoded_size: 130,
}),
getExpectedUpdatedArtifactBodyWhenUpdate: (): ArtifactBodyType => ({
entries: [
{
type: 'simple',
entries: [
{
field: 'process.executable',
operator: 'included',
type: 'exact_caseless',
value: 'c:\\randomFolder\\randomFile.exe, c:\\randomFolder\\randomFile2.exe',
},
],
},
],
}),
},
},
{
title: 'Event Filters',
pagePrefix: 'EventFiltersListPage',
create: {
formFields: [
{
type: 'input',
selector: 'eventFilters-form-name-input',
value: 'Event filter name',
},
{
type: 'input',
selector: 'eventFilters-form-description-input',
value: 'This is the event filter description',
},
{
type: 'click',
selector: 'fieldAutocompleteComboBox',
},
{
type: 'customClick',
selector: 'button[title="agent.ephemeral_id"]',
},
{
type: 'click',
selector: 'valuesAutocompleteMatch',
},
{
type: 'input',
selector: 'valuesAutocompleteMatch',
value: 'endpoint',
},
],
checkResults: [
{
selector: 'EventFiltersListPage-card-criteriaConditions-condition',
value: 'AND agent.ephemeral_idIS endpoint',
},
],
},
update: {
formFields: [
{
type: 'input',
selector: 'eventFilters-form-name-input',
value: 'Event filter name edited',
},
{
type: 'input',
selector: 'eventFilters-form-description-input',
value: 'This is the event filter description edited',
},
{
type: 'click',
selector: 'fieldAutocompleteComboBox',
},
{
type: 'input',
selector: 'fieldAutocompleteComboBox',
value: 'agent.id',
},
{
type: 'customClick',
selector: 'button[title="agent.id"]',
},
{
type: 'input',
selector: 'valuesAutocompleteMatch',
value: 'test super large value',
},
{
type: 'click',
selector: 'eventFilters-form-description-input',
},
],
checkResults: [
{
selector: 'EventFiltersListPage-card-criteriaConditions-condition',
value: 'AND agent.idIS test super large value',
},
{
selector: 'EventFiltersListPage-card-header-title',
value: 'Event filter name edited',
},
{
selector: 'EventFiltersListPage-card-description',
value: 'This is the event filter description edited',
},
],
waitForValue: 'AND agent.idIS test super large value',
},
delete: {
confirmSelector: 'EventFiltersListPage-deleteModal-submitButton',
card: 'EventFiltersListPage-card',
},
urlPath: 'event_filters',
pageObject: 'eventFilters',
fleetArtifact: {
identifier: 'endpoint-eventfilterlist-windows-v1',
type: 'eventfilterlist',
getExpectedUpdatedtArtifactWhenCreate: (): ArtifactElasticsearchProperties => ({
type: 'eventfilterlist',
identifier: 'endpoint-eventfilterlist-windows-v1',
body: 'eJxVzFEKwjAQRdG9vO/iArKVUsqQPHVgmoRkWpSSvYvFH3/PhXuC2ZuyI8wn/F2JgK5bNWL6a3elJQTIg9lvrE9ubGKrJkwolU28NARojrYnfvW340uir1H6hYfYfmlOtWh2jGUs4wOrCC+X',
package_name: 'endpoint',
created: '2000-01-01T00:00:00.000Z',
relative_url:
'/api/fleet/artifacts/endpoint-eventfilterlist-windows-v1/b3373c93ffc795d954f22c625c084dc5874a156ec0cb3d4af1c3dab0b965fa30',
compression_algorithm: 'zlib',
decoded_size: 136,
decoded_sha256: 'b3373c93ffc795d954f22c625c084dc5874a156ec0cb3d4af1c3dab0b965fa30',
encryption_algorithm: 'none',
encoded_sha256: 'cc9bc4e3cc2c2767c3f56b17ebf4901dbe7e82f15720d48c745370e028c5e887',
encoded_size: 108,
}),
getExpectedUpdatedArtifactBodyWhenCreate: (): ArtifactBodyType => ({
entries: [
{
type: 'simple',
entries: [
{
field: 'agent.ephemeral_id',
operator: 'included',
type: 'exact_cased',
value: 'endpoint',
},
],
},
],
}),
getExpectedUpdatedArtifactWhenUpdate: (): ArtifactElasticsearchProperties => ({
type: 'eventfilterlist',
identifier: 'endpoint-eventfilterlist-windows-v1',
body: 'eJxVzEEKwyAURdGtyBuHLsCtlFA++hoEa+T7LQnBvZc0nXR6LtwDLKaJDf5+wPZKeLT0qpmY/tozMUd4yMJitxQxYa1UsVXhkUrIPfLU34SbBHsEaV98S+6nGpu51ivVZdGF7gpjHvP4ADqUMJs=',
package_name: 'endpoint',
created: '2000-01-01T00:00:00.000Z',
relative_url:
'/api/fleet/artifacts/endpoint-eventfilterlist-windows-v1/e4f00c88380d2c429eeb2741ad19383b94d76f79744b098b095befc24003e158',
compression_algorithm: 'zlib',
decoded_size: 140,
decoded_sha256: 'e4f00c88380d2c429eeb2741ad19383b94d76f79744b098b095befc24003e158',
encryption_algorithm: 'none',
encoded_sha256: 'e371e2a28b59bd942ca7ef9665dae7c9b27409ad6f2ca3bff6357a98deb23c12',
encoded_size: 110,
}),
getExpectedUpdatedArtifactBodyWhenUpdate: (): ArtifactBodyType => ({
entries: [
{
type: 'simple',
entries: [
{
field: 'agent.id',
operator: 'included',
type: 'exact_cased',
value: 'test super large value',
},
],
},
],
}),
},
},
{
title: 'Blocklist',
pagePrefix: 'blocklistPage',
create: {
formFields: [
{
type: 'input',
selector: 'blocklist-form-name-input',
value: 'Blocklist name',
},
{
type: 'input',
selector: 'blocklist-form-description-input',
value: 'This is the blocklist description',
},
{
type: 'click',
selector: 'blocklist-form-field-select',
},
{
type: 'click',
selector: 'blocklist-form-file.hash.*',
},
{
type: 'input',
selector: 'blocklist-form-values-input',
value:
'A4370C0CF81686C0B696FA6261c9d3e0d810ae704ab8301839dffd5d5112f476,aedb279e378BED6C2DB3C9DC9e12ba635e0b391c,741462ab431a22233C787BAAB9B653C7',
},
{
type: 'click',
selector: 'blocklist-form-name-input',
},
],
checkResults: [
{
selector: 'blocklistPage-card-criteriaConditions',
value:
'OSIS Windows\nAND file.hash.*IS ONE OF\n741462ab431a22233c787baab9b653c7\naedb279e378bed6c2db3c9dc9e12ba635e0b391c\na4370c0cf81686c0b696fa6261c9d3e0d810ae704ab8301839dffd5d5112f476',
},
],
},
update: {
formFields: [
{
type: 'input',
selector: 'blocklist-form-name-input',
value: 'Blocklist name edited',
},
{
type: 'input',
selector: 'blocklist-form-description-input',
value: 'This is the blocklist description edited',
},
{
type: 'click',
selector: 'blocklist-form-field-select',
},
{
type: 'click',
selector: 'blocklist-form-file.path.caseless',
},
{
type: 'clear',
selector:
'blocklist-form-values-input-a4370c0cf81686c0b696fa6261c9d3e0d810ae704ab8301839dffd5d5112f476',
},
{
type: 'clear',
selector: 'blocklist-form-values-input-741462ab431a22233c787baab9b653c7',
},
{
type: 'clear',
selector: 'blocklist-form-values-input-aedb279e378bed6c2db3c9dc9e12ba635e0b391c',
},
{
type: 'input',
selector: 'blocklist-form-values-input',
value: 'c:\\randomFolder\\randomFile.exe, c:\\randomFolder\\randomFile2.exe',
},
{
type: 'click',
selector: 'blocklist-form-name-input',
},
],
checkResults: [
{
selector: 'blocklistPage-card-criteriaConditions',
value:
'OSIS Windows\nAND file.path.caselessIS ONE OF\nc:\\randomFolder\\randomFile.exe\nc:\\randomFolder\\randomFile2.exe',
},
{
selector: 'blocklistPage-card-header-title',
value: 'Blocklist name edited',
},
{
selector: 'blocklistPage-card-description',
value: 'This is the blocklist description edited',
},
],
waitForValue:
'OSIS Windows\nAND file.path.caselessIS ONE OF\nc:\\randomFolder\\randomFile.exe\nc:\\randomFolder\\randomFile2.exe',
},
delete: {
confirmSelector: 'blocklistDeletionConfirm',
card: 'blocklistCard',
},
pageObject: 'blocklist',
urlPath: 'blocklist',
fleetArtifact: {
identifier: 'endpoint-blocklist-windows-v1',
type: 'blocklist',
getExpectedUpdatedtArtifactWhenCreate: (): ArtifactElasticsearchProperties => ({
type: 'blocklist',
identifier: 'endpoint-blocklist-windows-v1',
relative_url:
'/api/fleet/artifacts/endpoint-blocklist-windows-v1/637f1e8795406904980ae2ab4a69cea967756571507f6bd7fc94cde0add20df2',
body: 'eJylzk1qw0AMQOG7aG3C/GpmfJVggkbSYIPjmNgpDcF3LxS66LLN+sHje4Eu+33SDfrzC/bnqtDDNl3XWaH71dqks0APbZr1NNI2nq4SoYPbqnfab3foYVp4fogKdD8n/STeL0ybyoWWJ3TwQfNDoT9DCjagoxq8Jeec95xyqkS1VIyeEwzHcHR/NW0j2TdQpFJdKupTrirITqrnIlzUukroo5rqi+V/41zEd3jBJ8OGW7aYkU3Fgo3QoeUiXo1ka0iTCVSzNzb7Iq1JlGitayHhN3s4vgDTjqDt',
encryption_algorithm: 'none',
package_name: 'endpoint',
encoded_size: 219,
encoded_sha256: 'e803c1ee6aec0885092bfd6c288839f42b31107dd6d0bb2c8e2d2b9f8fc8b293',
decoded_size: 501,
decoded_sha256: '637f1e8795406904980ae2ab4a69cea967756571507f6bd7fc94cde0add20df2',
compression_algorithm: 'zlib',
created: '2000-01-01T00:00:00.000Z',
}),
getExpectedUpdatedArtifactBodyWhenCreate: (): ArtifactBodyType => ({
entries: [
{
type: 'simple',
entries: [
{
field: 'file.hash.md5',
operator: 'included',
type: 'exact_cased_any',
value: ['741462ab431a22233c787baab9b653c7'],
},
],
},
{
type: 'simple',
entries: [
{
field: 'file.hash.sha1',
operator: 'included',
type: 'exact_cased_any',
value: ['aedb279e378bed6c2db3c9dc9e12ba635e0b391c'],
},
],
},
{
type: 'simple',
entries: [
{
field: 'file.hash.sha256',
operator: 'included',
type: 'exact_cased_any',
value: ['a4370c0cf81686c0b696fa6261c9d3e0d810ae704ab8301839dffd5d5112f476'],
},
],
},
],
}),
getExpectedUpdatedArtifactWhenUpdate: (): ArtifactElasticsearchProperties => ({
type: 'blocklist',
identifier: 'endpoint-blocklist-windows-v1',
relative_url:
'/api/fleet/artifacts/endpoint-blocklist-windows-v1/3ead6ce4e34cb4411083a44bfe813d9442d296981ee8d56e727e6cff14dea0f0',
body: 'eJx9jUEKwjAURK8isw4uXOYAXqKV8kmmGPhNQpJKS/HuEkHBjcxqmMebA4ytBFbY4UDbM2FRw5KVMD/bHKgeFnNQnrO0OwxSZpGWCixCdLp6epiPhZu4NjmpVNY6Sdxh8BBdCTvA2XEsEn1arkk9y7d1Pbf+fvrHXN7Q7dnzAojqRb8=',
encryption_algorithm: 'none',
package_name: 'endpoint',
encoded_size: 131,
encoded_sha256: 'f0e2dc2aa8d968b704baa11bf3100db91a85991d5de431f8c389b7417335a701',
decoded_size: 197,
decoded_sha256: '3ead6ce4e34cb4411083a44bfe813d9442d296981ee8d56e727e6cff14dea0f0',
compression_algorithm: 'zlib',
created: '2000-01-01T00:00:00.000Z',
}),
getExpectedUpdatedArtifactBodyWhenUpdate: (): ArtifactBodyType => ({
entries: [
{
type: 'simple',
entries: [
{
field: 'file.path',
operator: 'included',
type: 'exact_caseless_any',
value: ['c:\\randomFolder\\randomFile.exe', ' c:\\randomFolder\\randomFile2.exe'],
},
],
},
],
}),
},
},
{
title: 'Host isolation exceptions',
pagePrefix: 'hostIsolationExceptionsListPage',
create: {
formFields: [
{
type: 'input',
selector: 'hostIsolationExceptions-form-name-input',
value: 'Host Isolation exception name',
},
{
type: 'input',
selector: 'hostIsolationExceptions-form-description-input',
value: 'This is the host isolation exception description',
},
{
type: 'input',
selector: 'hostIsolationExceptions-form-ip-input',
value: '1.1.1.1',
},
],
checkResults: [
{
selector: 'hostIsolationExceptionsListPage-card-criteriaConditions',
value: 'OSIS Windows, Linux, Mac\nAND destination.ipIS 1.1.1.1',
},
],
},
update: {
formFields: [
{
type: 'input',
selector: 'hostIsolationExceptions-form-name-input',
value: 'Host Isolation exception name edited',
},
{
type: 'input',
selector: 'hostIsolationExceptions-form-description-input',
value: 'This is the host isolation exception description edited',
},
{
type: 'input',
selector: 'hostIsolationExceptions-form-ip-input',
value: '2.2.2.2/24',
},
],
checkResults: [
{
selector: 'hostIsolationExceptionsListPage-card-criteriaConditions',
value: 'OSIS Windows, Linux, Mac\nAND destination.ipIS 2.2.2.2/24',
},
{
selector: 'hostIsolationExceptionsListPage-card-header-title',
value: 'Host Isolation exception name edited',
},
{
selector: 'hostIsolationExceptionsListPage-card-description',
value: 'This is the host isolation exception description edited',
},
],
waitForValue: 'OSIS Windows, Linux, Mac\nAND destination.ipIS 2.2.2.2/24',
},
delete: {
confirmSelector: 'hostIsolationExceptionsDeletionConfirm',
card: 'hostIsolationExceptionsCard',
},
pageObject: 'hostIsolationExceptions',
urlPath: 'host_isolation_exceptions',
fleetArtifact: {
identifier: 'endpoint-hostisolationexceptionlist-windows-v1',
type: 'hostisolationexceptionlist',
getExpectedUpdatedtArtifactWhenCreate: (): ArtifactElasticsearchProperties => ({
type: 'hostisolationexceptionlist',
identifier: 'endpoint-hostisolationexceptionlist-windows-v1',
relative_url:
'/api/fleet/artifacts/endpoint-hostisolationexceptionlist-windows-v1/2c3ee2b5e7f86f8c336a3df7e59a1151b11d7eec382442032e69712d6a6459e0',
body: 'eJxVjEEKgzAUBe/y1kFwm6uIyCd5hQ9pEpKvWCR3LxVclNnNwFxgtqbs8MsF+1TCo+u7JsL9tZcyRXhEdtMspiVPWuFQKptYafDQHNIeGeGeFU8JtgXptzwk7T87TzcY61jHF647LBE=',
encryption_algorithm: 'none',
package_name: 'endpoint',
encoded_size: 104,
encoded_sha256: 'f958ada742a0be63d136901317c6bfd04b2ab5f52cdd0e872461089b0009bb3e',
decoded_size: 131,
decoded_sha256: '2c3ee2b5e7f86f8c336a3df7e59a1151b11d7eec382442032e69712d6a6459e0',
compression_algorithm: 'zlib',
created: '2000-01-01T00:00:00.000Z',
}),
getExpectedUpdatedArtifactBodyWhenCreate: (): ArtifactBodyType => ({
entries: [
{
type: 'simple',
entries: [
{
field: 'destination.ip',
operator: 'included',
type: 'exact_cased',
value: '1.1.1.1',
},
],
},
],
}),
getExpectedUpdatedArtifactWhenUpdate: (): ArtifactElasticsearchProperties => ({
type: 'hostisolationexceptionlist',
identifier: 'endpoint-hostisolationexceptionlist-windows-v1',
relative_url:
'/api/fleet/artifacts/endpoint-hostisolationexceptionlist-windows-v1/4b62473b4cf057277b3297896771cc1061c3bea2c4f7ec1ef5c2546f33d5d9e8',
body: 'eJxVjEEKwyAUBe/y1pJC6MqrlBA++gofrIr+hJbg3UsCXZTZzcAcYLam7PCPA/aphEfXV02E+2tPZYrwiOymWUxLnrTCoVQ2sdLgoTmkLTLC/VZ8S7A1SL/kLmk77Txd3OY7xjKW8QUwWyyq',
encryption_algorithm: 'none',
package_name: 'endpoint',
encoded_size: 108,
encoded_sha256: '84df618343078f43a54299bcebef03010f3ec4ffdf7160448882fee9bafa1adb',
decoded_size: 134,
decoded_sha256: '4b62473b4cf057277b3297896771cc1061c3bea2c4f7ec1ef5c2546f33d5d9e8',
compression_algorithm: 'zlib',
created: '2000-01-01T00:00:00.000Z',
}),
getExpectedUpdatedArtifactBodyWhenUpdate: (): ArtifactBodyType => ({
entries: [
{
type: 'simple',
entries: [
{
field: 'destination.ip',
operator: 'included',
type: 'exact_cased',
value: '2.2.2.2/24',
},
],
},
],
}),
},
},
];
export const getCreateMultipleData = () => ({
title: 'Trusted applications',
pagePrefix: 'trustedAppsListPage',
create: {
formFields: [
{
type: 'input',
selector: 'trustedApps-form-descriptionField',
value: 'This is the trusted application description',
},
{
type: 'input',
selector: 'trustedApps-form-nameTextField',
value: 'Trusted application name',
},
{
type: 'click',
selector: 'trustedApps-form-conditionsBuilder-group1-entry0-field',
},
{
type: 'click',
selector: 'trustedApps-form-conditionsBuilder-group1-entry0-field-type-Path',
},
{
type: 'input',
selector: 'trustedApps-form-conditionsBuilder-group1-entry0-value',
value: 'c:\\randomFolder\\randomFile.exe',
},
],
},
urlPath: 'trusted_apps',
pageObject: 'trustedApps',
fleetArtifact: {
identifier: 'endpoint-trustlist-windows-v1',
type: 'trustedApplications',
getExpectedUpdatedArtifactWhenCreateMultipleFirst: (): ArtifactElasticsearchProperties => ({
type: 'trustlist',
identifier: 'endpoint-trustlist-windows-v1',
body: 'eJzNjlEKwjAQBe+y38ED5ABewhaJySsubJuwu5VK6d0lgoI38PMxj2F2wuLKMIqXnfzZQJGM5yag8MMmhhSK1LRmmJ2wIa+ebu9jbdDkVSkSL1nWgkLho8OWsl9zMgjMKNAjydpBjsOgaSl1Plcp0O9iQff7nbXQMR7h79ImVvOeNh4vUR5zdA==',
package_name: 'endpoint',
created: '2000-01-01T00:00:00.000Z',
relative_url:
'/api/fleet/artifacts/endpoint-trustlist-windows-v1/329fc9176a24d64f4376d2c25d5db5b31cf86b288dac83c8a004dfe5bbfdc7d0',
compression_algorithm: 'zlib',
decoded_size: 323,
decoded_sha256: '329fc9176a24d64f4376d2c25d5db5b31cf86b288dac83c8a004dfe5bbfdc7d0',
encryption_algorithm: 'none',
encoded_sha256: '4d9eecb830948eabd721563fd2473900207d043126e66eac2ef78f9e05a80adb',
encoded_size: 136,
}),
getExpectedUpdatedArtifactBodyWhenCreateMultipleFirst: (
firstSuffix: string,
secondSuffix: string
): ArtifactBodyType => ({
entries: [
{
type: 'simple',
entries: [
{
field: 'process.executable',
operator: 'included',
type: 'exact_caseless',
value: `c:\\randomFolder\\randomFile.exe${firstSuffix}`,
},
],
},
{
entries: [
{
field: 'process.executable',
operator: 'included',
type: 'exact_caseless',
value: `c:\\randomFolder\\randomFile.exe${secondSuffix}`,
},
],
type: 'simple',
},
],
}),
getExpectedUpdatedArtifactWhenCreateMultipleSecond: (): ArtifactElasticsearchProperties => ({
type: 'trustlist',
identifier: 'endpoint-trustlist-windows-v1',
body: 'eJzNjlEKwjAQRO8y38ED5ABewhaJyYiBbRJ2U6mU3l1aUPAGfg5veLwVLF0zDf6yor8a4WF5akK4H3bPlASPpjXS7MSFce7hdhxro4ZeFR65RJkTE9xHxyXEfo3BKDSDwzPIvIPoh0FDSXU6V0nU78rC3d8fWRO2cXN/l2aMtRxt4/YGxIFzyA==',
package_name: 'endpoint',
created: '2000-01-01T00:00:00.000Z',
relative_url:
'/api/fleet/artifacts/endpoint-trustlist-windows-v1/3be2ce848f9b49d6531e6dc80f43579e00adbc640d3f785c14c8f9fa2652500a',
compression_algorithm: 'zlib',
decoded_size: 324,
decoded_sha256: '3be2ce848f9b49d6531e6dc80f43579e00adbc640d3f785c14c8f9fa2652500a',
encryption_algorithm: 'none',
encoded_sha256: '68304c35bbe863d0fbb15cf7e5ae5c84bad17aa7a3bc26828f9f0b20e0df6ed8',
encoded_size: 136,
}),
getExpectedUpdatedArtifactBodyWhenCreateMultipleSecond: (
firstSuffix: string,
secondSuffix: string
): ArtifactBodyType => ({
entries: [
{
type: 'simple',
entries: [
{
field: 'process.executable',
operator: 'included',
type: 'exact_caseless',
value: `c:\\randomFolder\\randomFile.exe${firstSuffix}`,
},
],
},
{
entries: [
{
field: 'process.executable',
operator: 'included',
type: 'exact_caseless',
value: `c:\\randomFolder\\randomFile.exe${secondSuffix}`,
},
],
type: 'simple',
},
],
}),
},
});

View file

@ -1,34 +0,0 @@
/*
* 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 { resolve } from 'path';
import { FtrConfigProviderContext } from '@kbn/test';
import { generateConfig } from './config.base';
import { services } from '../services';
export default async function (ftrConfigProviderContext: FtrConfigProviderContext) {
const { readConfigFile } = ftrConfigProviderContext;
const xpackFunctionalConfig = await readConfigFile(
require.resolve('../../../../functional/config.base.js')
);
return generateConfig({
ftrConfigProviderContext,
baseConfig: xpackFunctionalConfig,
testFiles: [resolve(__dirname, '../apps/integrations_feature_flag')],
junitReportName:
'X-Pack Endpoint Integrations With Feature Flags turned on Functional Tests on ESS',
target: 'ess',
kbnServerArgs: [
// set the packagerTaskInterval to 5s in order to speed up test executions when checking fleet artifacts
'--xpack.securitySolution.packagerTaskInterval=5s',
`--xpack.securitySolution.enableExperimental=${JSON.stringify(['unifiedManifestEnabled'])}`,
],
services,
});
}

View file

@ -1,35 +0,0 @@
/*
* 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 { resolve } from 'path';
import { FtrConfigProviderContext } from '@kbn/test';
import { generateConfig } from './config.base';
import { svlServices } from '../services';
export default async function (ftrConfigProviderContext: FtrConfigProviderContext) {
const { readConfigFile } = ftrConfigProviderContext;
const svlBaseConfig = await readConfigFile(
require.resolve('../../../../../test_serverless/shared/config.base.ts')
);
return generateConfig({
ftrConfigProviderContext,
baseConfig: svlBaseConfig,
testFiles: [resolve(__dirname, '../apps/integrations_feature_flag')],
junitReportName:
'X-Pack Endpoint Integrations With Feature Flags turned on Functional Tests on ESS',
target: 'serverless',
kbnServerArgs: [
'--serverless=security',
// set the packagerTaskInterval to 5s in order to speed up test executions when checking fleet artifacts
'--xpack.securitySolution.packagerTaskInterval=5s',
`--xpack.securitySolution.enableExperimental=${JSON.stringify(['unifiedManifestEnabled'])}`,
],
services: svlServices,
});
}

View file

@ -20,8 +20,7 @@ import { HOST_ISOLATION_EXCEPTIONS_LIST_DEFINITION } from '@kbn/security-solutio
import { BLOCKLISTS_LIST_DEFINITION } from '@kbn/security-solution-plugin/public/management/pages/blocklist/constants';
import { ManifestConstants } from '@kbn/security-solution-plugin/server/endpoint/lib/artifacts';
import { FtrService } from '../../../../functional/ftr_provider_context';
import { InternalManifestSchemaResponseType } from '../apps/integrations/mocks';
import { InternalUnifiedManifestSchemaResponseType } from '../apps/integrations_feature_flag/mocks';
import { InternalUnifiedManifestSchemaResponseType } from '../apps/integrations/mocks';
export interface ArtifactTestData {
artifact: ExceptionListItemSchema;
@ -123,19 +122,6 @@ export class EndpointArtifactsTestResources extends FtrService {
return this.createExceptionItem(blocklist);
}
async getArtifacts() {
const {
hits: { hits: manifestResults },
} = await this.esClient.search({
index: '.kibana*',
query: { bool: { filter: [{ term: { type: ManifestConstants.SAVED_OBJECT_TYPE } }] } },
size: 1,
});
const manifestResult = manifestResults[0] as InternalManifestSchemaResponseType;
return manifestResult._source['endpoint:user-artifact-manifest'].artifacts;
}
async getArtifactsFromUnifiedManifestSO(): Promise<
Array<
InternalUnifiedManifestSchemaResponseType['_source']['endpoint:unified-user-artifact-manifest']