mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[EDR Workflows] Unified Manifest - Manifest Manager (#179698)
This pull request introduces a new SO used for keeping relations between artifactId and policyId. It addresses an issue where some users found the single SO structure containing too many nested entries. Originally, we planned to rewrite the existing Manifest Manager. However, during the POC implementation, it became clear that the effort required to refactor and retest the existing solution would be substantial. Therefore, this pull request can be considered as the first step in transitioning our approach from one SO to this new, distributed one. The main idea behind these changes is to modify the structure of the SO, rather than the logic of the Manifest Manager. To accomplish this, we need to retrieve the SO from the new source, translate it into the existing SO format (many SOs to one), execute the unchanged operations of the Manifest Manager on artifacts, translate the resulting SO into multiple SOs, and save them. This change is expected to be deployed with a Feature Flag, and we need to ensure that everything continues to function correctly in both cases. Therefore, I've introduced a new FTR suite with the Feature Flag enabled, which should be run alongside tests with the Feature Flag disabled. This suite contains duplicated test files that depend on SO logic. When we activate the Feature Flag, these tests should replace the existing ones, as the Unified Manifest SO will become the default approach. It appears that there is no need to introduce any kind of migrations, as the Manifest Manager is capable of recreating missing SOs (which has been tested). --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
bcbd55088c
commit
3d1d2a5d43
25 changed files with 3411 additions and 976 deletions
|
@ -403,7 +403,9 @@ enabled:
|
|||
- x-pack/test/security_solution_endpoint/endpoint.config.ts
|
||||
- x-pack/test/security_solution_endpoint/serverless.endpoint.config.ts
|
||||
- x-pack/test/security_solution_endpoint/integrations.config.ts
|
||||
- x-pack/test/security_solution_endpoint/integrations_feature_flag.config.ts
|
||||
- x-pack/test/security_solution_endpoint/serverless.integrations.config.ts
|
||||
- x-pack/test/security_solution_endpoint/serverless.integrations_feature_flag.config.ts
|
||||
- x-pack/test/session_view/basic/config.ts
|
||||
- x-pack/test/spaces_api_integration/security_and_spaces/config_basic.ts
|
||||
- x-pack/test/spaces_api_integration/security_and_spaces/copy_to_space_config_basic.ts
|
||||
|
|
|
@ -157,6 +157,7 @@ export const HASH_TO_VERSION_MAP = {
|
|||
'core-usage-stats|3d1b76c39bfb2cc8296b024d73854724': '7.14.1',
|
||||
'csp-rule-template|6ee70dc06c0ca3ddffc18222f202ab25': '10.0.0',
|
||||
'dashboard|b8aa800aa5e0d975c5e8dc57f03d41f8': '10.2.0',
|
||||
'endpoint:unified-user-artifact-manifest|393c6e4f5f16288c24ef9057e4d76a4c': '10.0.0',
|
||||
'endpoint:user-artifact-manifest|7502b5c5bc923abe8aa5ccfd636e8c3d': '10.0.0',
|
||||
'enterprise_search_telemetry|3d1b76c39bfb2cc8296b024d73854724': '10.0.0',
|
||||
'epm-packages-assets|44621b2f6052ef966da47b7c3a00f33b': '10.0.0',
|
||||
|
|
|
@ -280,6 +280,11 @@
|
|||
"title",
|
||||
"version"
|
||||
],
|
||||
"endpoint:unified-user-artifact-manifest": [
|
||||
"artifactIds",
|
||||
"policyId",
|
||||
"semanticVersion"
|
||||
],
|
||||
"endpoint:user-artifact-manifest": [
|
||||
"artifacts",
|
||||
"schemaVersion"
|
||||
|
|
|
@ -964,6 +964,20 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"endpoint:unified-user-artifact-manifest": {
|
||||
"dynamic": false,
|
||||
"properties": {
|
||||
"artifactIds": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"policyId": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"semanticVersion": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"endpoint:user-artifact-manifest": {
|
||||
"dynamic": false,
|
||||
"properties": {
|
||||
|
|
|
@ -85,6 +85,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
|
|||
"core-usage-stats": "b3c04da317c957741ebcdedfea4524049fdc79ff",
|
||||
"csp-rule-template": "c151324d5f85178169395eecb12bac6b96064654",
|
||||
"dashboard": "211e9ca30f5a95d5f3c27b1bf2b58e6cfa0c9ae9",
|
||||
"endpoint:unified-user-artifact-manifest": "71c7fcb52c658b21ea2800a6b6a76972ae1c776e",
|
||||
"endpoint:user-artifact-manifest": "1c3533161811a58772e30cdc77bac4631da3ef2b",
|
||||
"enterprise_search_telemetry": "9ac912e1417fc8681e0cd383775382117c9e3d3d",
|
||||
"epm-packages": "f8ee125b57df31fd035dc04ad81aef475fd2f5bd",
|
||||
|
|
|
@ -49,6 +49,7 @@ const previouslyRegisteredTypes = [
|
|||
'event-annotation-group',
|
||||
'endpoint:user-artifact',
|
||||
'endpoint:user-artifact-manifest',
|
||||
'endpoint:unified-user-artifact-manifest',
|
||||
'enterprise_search_telemetry',
|
||||
'epm-packages',
|
||||
'epm-packages-assets',
|
||||
|
|
|
@ -205,6 +205,7 @@ describe('split .kibana index into multiple system indices', () => {
|
|||
"connector_token",
|
||||
"core-usage-stats",
|
||||
"csp-rule-template",
|
||||
"endpoint:unified-user-artifact-manifest",
|
||||
"endpoint:user-artifact-manifest",
|
||||
"enterprise_search_telemetry",
|
||||
"epm-packages",
|
||||
|
|
|
@ -252,6 +252,11 @@ export const allowedExperimentalValues = Object.freeze({
|
|||
*/
|
||||
malwareOnWriteScanOptionAvailable: true,
|
||||
|
||||
/**
|
||||
* Enables unified manifest that replaces existing user artifacts manifest SO with a new approach of creating a SO per package policy.
|
||||
*/
|
||||
unifiedManifestEnabled: false,
|
||||
|
||||
/**
|
||||
* Enables Security AI Assistant's Flyout mode
|
||||
*/
|
||||
|
|
|
@ -170,7 +170,6 @@ export class ManifestTask {
|
|||
this.logger.error(
|
||||
`unable to recover from error while attempting to retrieve last computed manifest`
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,6 +109,7 @@ export const internalUnifiedManifestSchema = t.intersection([
|
|||
t.type({
|
||||
id: identifier,
|
||||
created: t.union([t.string, t.undefined]),
|
||||
version: t.union([t.string, t.undefined]),
|
||||
})
|
||||
),
|
||||
]);
|
||||
|
|
|
@ -72,6 +72,7 @@ export interface ManifestManagerMockOptions {
|
|||
packagePolicyService: jest.Mocked<PackagePolicyClient>;
|
||||
savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
|
||||
productFeaturesService: ProductFeaturesService;
|
||||
experimentalFeatures?: string[];
|
||||
}
|
||||
|
||||
export const buildManifestManagerMockOptions = (
|
||||
|
@ -98,7 +99,8 @@ export const buildManifestManagerContextMock = (
|
|||
...fullOpts,
|
||||
artifactClient: createEndpointArtifactClientMock(),
|
||||
logger: loggingSystemMock.create().get() as jest.Mocked<Logger>,
|
||||
experimentalFeatures: parseExperimentalConfigValue([]).features,
|
||||
experimentalFeatures: parseExperimentalConfigValue([...(opts.experimentalFeatures ?? [])])
|
||||
.features,
|
||||
packagerTaskPackagePolicyUpdateBatchSize: 10,
|
||||
esClient: elasticsearchServiceMock.createElasticsearchClient(),
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -6,15 +6,17 @@
|
|||
*/
|
||||
|
||||
import semver from 'semver';
|
||||
import { isEmpty, isEqual, keyBy } from 'lodash';
|
||||
import { chunk, isEmpty, isEqual, keyBy } from 'lodash';
|
||||
import type { ElasticsearchClient } from '@kbn/core/server';
|
||||
import { type Logger, type SavedObjectsClientContract } from '@kbn/core/server';
|
||||
import { ENDPOINT_LIST_ID, ENDPOINT_ARTIFACT_LISTS } from '@kbn/securitysolution-list-constants';
|
||||
import { ENDPOINT_ARTIFACT_LISTS, ENDPOINT_LIST_ID } from '@kbn/securitysolution-list-constants';
|
||||
import type { 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 { ProductFeatureKey } from '@kbn/security-solution-features/keys';
|
||||
import { asyncForEach } from '@kbn/std';
|
||||
import { UnifiedManifestClient } from '../unified_manifest_client';
|
||||
import { stringify } from '../../../utils/stringify';
|
||||
import { QueueProcessor } from '../../../utils/queue_processor';
|
||||
import type { ProductFeaturesService } from '../../../../lib/product_features_service/product_features_service';
|
||||
|
@ -35,9 +37,15 @@ import {
|
|||
Manifest,
|
||||
} from '../../../lib/artifacts';
|
||||
|
||||
import type {
|
||||
InternalUnifiedManifestBaseSchema,
|
||||
InternalUnifiedManifestSchema,
|
||||
InternalUnifiedManifestUpdateSchema,
|
||||
} from '../../../schemas/artifacts';
|
||||
import {
|
||||
internalArtifactCompleteSchema,
|
||||
type InternalArtifactCompleteSchema,
|
||||
type InternalManifestSchema,
|
||||
type WrappedTranslatedExceptionList,
|
||||
} from '../../../schemas/artifacts';
|
||||
import type { EndpointArtifactClientInterface } from '../artifact_client';
|
||||
|
@ -513,7 +521,25 @@ export class ManifestManager {
|
|||
*/
|
||||
public async getLastComputedManifest(): Promise<Manifest | null> {
|
||||
try {
|
||||
const manifestSo = await this.getManifestClient().getManifest();
|
||||
let manifestSo;
|
||||
if (this.experimentalFeatures.unifiedManifestEnabled) {
|
||||
const unifiedManifestsSo = await this.getAllUnifiedManifestsSO();
|
||||
// On first run, there will be no existing Unified Manifests SO, so we need to copy the semanticVersion from the legacy manifest
|
||||
// This is to ensure that the first Unified Manifest created has the same semanticVersion as the legacy manifest and is not too far
|
||||
// behind for package policy to pick it up.
|
||||
if (unifiedManifestsSo.length === 0) {
|
||||
const legacyManifestSo = await this.getManifestClient().getManifest();
|
||||
const legacySemanticVersion = legacyManifestSo?.attributes?.semanticVersion;
|
||||
manifestSo = this.transformUnifiedManifestSOtoLegacyManifestSO(
|
||||
unifiedManifestsSo,
|
||||
legacySemanticVersion
|
||||
);
|
||||
} else {
|
||||
manifestSo = this.transformUnifiedManifestSOtoLegacyManifestSO(unifiedManifestsSo);
|
||||
}
|
||||
} else {
|
||||
manifestSo = await this.getManifestClient().getManifest();
|
||||
}
|
||||
|
||||
if (manifestSo.version === undefined) {
|
||||
throw new InvalidInternalManifestError(
|
||||
|
@ -721,21 +747,25 @@ export class ManifestManager {
|
|||
* @returns {Promise<Error | null>} An error, if encountered, or null.
|
||||
*/
|
||||
public async commit(manifest: Manifest) {
|
||||
const manifestClient = this.getManifestClient();
|
||||
|
||||
// Commit the new manifest
|
||||
const manifestSo = manifest.toSavedObject();
|
||||
const version = manifest.getSavedObjectVersion();
|
||||
|
||||
if (version == null) {
|
||||
await manifestClient.createManifest(manifestSo);
|
||||
if (this.experimentalFeatures.unifiedManifestEnabled) {
|
||||
await this.commitUnified(manifestSo);
|
||||
} else {
|
||||
await manifestClient.updateManifest(manifestSo, {
|
||||
version,
|
||||
});
|
||||
}
|
||||
const manifestClient = this.getManifestClient();
|
||||
|
||||
this.logger.debug(`Committed manifest ${manifest.getSemanticVersion()}`);
|
||||
const version = manifest.getSavedObjectVersion();
|
||||
|
||||
if (version == null) {
|
||||
await manifestClient.createManifest(manifestSo);
|
||||
} else {
|
||||
await manifestClient.updateManifest(manifestSo, {
|
||||
version,
|
||||
});
|
||||
}
|
||||
|
||||
this.logger.debug(`Committed manifest ${manifest.getSemanticVersion()}`);
|
||||
}
|
||||
}
|
||||
|
||||
private fetchAllPolicies(): AsyncIterable<PackagePolicy[]> {
|
||||
|
@ -835,4 +865,229 @@ export class ManifestManager {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unified Manifest methods
|
||||
*/
|
||||
|
||||
private setNewSemanticVersion(semanticVersion: string): string | null {
|
||||
const newSemanticVersion = semver.inc(semanticVersion, 'patch');
|
||||
if (!semver.valid(newSemanticVersion)) {
|
||||
throw new Error(`Invalid semver: ${newSemanticVersion}`);
|
||||
}
|
||||
return newSemanticVersion;
|
||||
}
|
||||
|
||||
protected getUnifiedManifestClient(): UnifiedManifestClient {
|
||||
return new UnifiedManifestClient(this.savedObjectsClient);
|
||||
}
|
||||
|
||||
public async getAllUnifiedManifestsSO(): Promise<InternalUnifiedManifestSchema[]> {
|
||||
return this.getUnifiedManifestClient().getAllUnifiedManifests();
|
||||
}
|
||||
|
||||
public transformUnifiedManifestSOtoLegacyManifestSO(
|
||||
unifiedManifestsSo: InternalUnifiedManifestSchema[],
|
||||
semanticVersion?: string
|
||||
): {
|
||||
version: string;
|
||||
attributes: {
|
||||
artifacts: Array<
|
||||
{ artifactId: string; policyId: undefined } | { artifactId: string; policyId: string }
|
||||
>;
|
||||
semanticVersion: string;
|
||||
schemaVersion: ManifestSchemaVersion;
|
||||
};
|
||||
} {
|
||||
const globalUnifiedManifest = unifiedManifestsSo.find((a) => a.policyId === '.global');
|
||||
return {
|
||||
version: 'WzQ3NzAsMV0=', // version is hardcoded since it was used only to determine whether to create a new manifest or update an existing one
|
||||
attributes: {
|
||||
artifacts: [
|
||||
...(globalUnifiedManifest?.artifactIds.map((artifactId) => ({
|
||||
artifactId,
|
||||
policyId: undefined,
|
||||
})) ?? []),
|
||||
...unifiedManifestsSo.reduce(
|
||||
(acc: Array<{ artifactId: string; policyId: string }>, unifiedManifest) => {
|
||||
if (unifiedManifest.policyId === '.global') {
|
||||
return acc;
|
||||
}
|
||||
acc.push(
|
||||
...unifiedManifest.artifactIds.map((artifactId) => ({
|
||||
policyId: unifiedManifest.policyId,
|
||||
artifactId,
|
||||
}))
|
||||
);
|
||||
|
||||
return acc;
|
||||
},
|
||||
[]
|
||||
),
|
||||
],
|
||||
semanticVersion: (semanticVersion || globalUnifiedManifest?.semanticVersion) ?? '1.0.0',
|
||||
schemaVersion: this.schemaVersion,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public transformLegacyManifestSOtoUnifiedManifestSO(
|
||||
manifestSo: InternalManifestSchema,
|
||||
unifiedManifestsSo: InternalUnifiedManifestSchema[]
|
||||
): Array<InternalUnifiedManifestBaseSchema & { id?: string }> {
|
||||
const manifestObject = manifestSo.artifacts.reduce(
|
||||
(
|
||||
acc: Record<string, InternalUnifiedManifestBaseSchema & { id?: string }>,
|
||||
{ artifactId, policyId = '.global' }
|
||||
) => {
|
||||
const existingPolicy = acc[policyId];
|
||||
if (existingPolicy) {
|
||||
existingPolicy.artifactIds.push(artifactId);
|
||||
} else {
|
||||
const existingUnifiedManifestSo = unifiedManifestsSo.find(
|
||||
(item) => item.policyId === policyId
|
||||
);
|
||||
|
||||
// On first run, there will be no existing Unified Manifests SO, so we need to copy the semanticVersion from the legacy manifest
|
||||
// This is to ensure that the first Unified Manifest created has the same semanticVersion as the legacy manifest and is not too far
|
||||
// behind for package policy to pick it up.
|
||||
const semanticVersion =
|
||||
(policyId === '.global' && !unifiedManifestsSo.length
|
||||
? manifestSo?.semanticVersion
|
||||
: existingUnifiedManifestSo?.semanticVersion) ?? '1.0.0';
|
||||
|
||||
acc[policyId] = {
|
||||
policyId,
|
||||
artifactIds: [artifactId],
|
||||
semanticVersion,
|
||||
id: existingUnifiedManifestSo?.id,
|
||||
};
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
return Object.values(manifestObject);
|
||||
}
|
||||
|
||||
public prepareUnifiedManifestsSOUpdates(
|
||||
unifiedManifestsSo: Array<Omit<InternalUnifiedManifestUpdateSchema, 'id'> & { id?: string }>,
|
||||
existingUnifiedManifestsSo: InternalUnifiedManifestSchema[]
|
||||
) {
|
||||
const existingManifestsObj: Record<string, InternalUnifiedManifestSchema> = {};
|
||||
existingUnifiedManifestsSo.forEach((manifest) => {
|
||||
existingManifestsObj[manifest.id] = manifest;
|
||||
});
|
||||
|
||||
const { unifiedManifestsToUpdate, unifiedManifestsToCreate } = unifiedManifestsSo.reduce(
|
||||
(
|
||||
acc: {
|
||||
unifiedManifestsToUpdate: InternalUnifiedManifestUpdateSchema[];
|
||||
unifiedManifestsToCreate: InternalUnifiedManifestBaseSchema[];
|
||||
},
|
||||
unifiedManifest
|
||||
) => {
|
||||
if (unifiedManifest.id !== undefined) {
|
||||
// Manifest with id exists in SO, check if it needs to be updated
|
||||
const existingUnifiedManifest = existingManifestsObj[unifiedManifest.id];
|
||||
// Update SO if the artifactIds changed.
|
||||
if (!isEqual(existingUnifiedManifest.artifactIds, unifiedManifest.artifactIds)) {
|
||||
acc.unifiedManifestsToUpdate.push({
|
||||
...unifiedManifest,
|
||||
semanticVersion: this.setNewSemanticVersion(unifiedManifest.semanticVersion),
|
||||
version: existingUnifiedManifest.version,
|
||||
} as InternalUnifiedManifestUpdateSchema);
|
||||
}
|
||||
} else {
|
||||
// Manifest with id does not exist in SO, create new SO
|
||||
acc.unifiedManifestsToCreate.push(unifiedManifest);
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{ unifiedManifestsToUpdate: [], unifiedManifestsToCreate: [] }
|
||||
);
|
||||
|
||||
const unifiedManifestsToDelete = existingUnifiedManifestsSo.reduce(
|
||||
(acc: string[], { policyId, id }) => {
|
||||
const existingPolicy = unifiedManifestsSo.find((item) => item.policyId === policyId);
|
||||
if (!existingPolicy) {
|
||||
acc.push(id);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return { unifiedManifestsToUpdate, unifiedManifestsToCreate, unifiedManifestsToDelete };
|
||||
}
|
||||
|
||||
public async bumpGlobalUnifiedManifestVersion(): Promise<void> {
|
||||
const globalUnifiedManifestSO =
|
||||
await this.getUnifiedManifestClient().getUnifiedManifestByPolicyId('.global');
|
||||
if (!globalUnifiedManifestSO?.saved_objects?.length) {
|
||||
this.logger.warn('No Global Unified Manifest found to bump version');
|
||||
return;
|
||||
}
|
||||
const globalUnifiedManifest = globalUnifiedManifestSO.saved_objects[0];
|
||||
|
||||
const newSemanticVersion =
|
||||
this.setNewSemanticVersion(globalUnifiedManifest.attributes.semanticVersion) || '1.0.0';
|
||||
await this.getUnifiedManifestClient().updateUnifiedManifest({
|
||||
...globalUnifiedManifest.attributes,
|
||||
id: globalUnifiedManifest.id,
|
||||
semanticVersion: newSemanticVersion,
|
||||
});
|
||||
}
|
||||
|
||||
public async commitUnified(manifestSo: InternalManifestSchema): Promise<void> {
|
||||
const existingUnifiedManifestsSo = await this.getAllUnifiedManifestsSO();
|
||||
|
||||
const unifiedManifestSO = this.transformLegacyManifestSOtoUnifiedManifestSO(
|
||||
manifestSo,
|
||||
existingUnifiedManifestsSo
|
||||
);
|
||||
|
||||
const { unifiedManifestsToUpdate, unifiedManifestsToCreate, unifiedManifestsToDelete } =
|
||||
this.prepareUnifiedManifestsSOUpdates(unifiedManifestSO, existingUnifiedManifestsSo);
|
||||
|
||||
if (unifiedManifestsToCreate.length) {
|
||||
await asyncForEach(chunk(unifiedManifestsToCreate, 100), async (unifiedManifestsBatch) => {
|
||||
await this.getUnifiedManifestClient().createUnifiedManifests(unifiedManifestsBatch);
|
||||
});
|
||||
this.logger.debug(`Created ${unifiedManifestsToCreate.length} unified manifests`);
|
||||
}
|
||||
|
||||
if (unifiedManifestsToUpdate.length) {
|
||||
await asyncForEach(chunk(unifiedManifestsToUpdate, 100), async (unifiedManifestsBatch) => {
|
||||
await this.getUnifiedManifestClient().updateUnifiedManifests(unifiedManifestsBatch);
|
||||
});
|
||||
|
||||
this.logger.debug(`Updated ${unifiedManifestsToUpdate.length} unified manifests`);
|
||||
}
|
||||
|
||||
if (unifiedManifestsToDelete.length) {
|
||||
await asyncForEach(chunk(unifiedManifestsToDelete, 100), async (unifiedManifestsBatch) => {
|
||||
await this.getUnifiedManifestClient().deleteUnifiedManifestByIds(unifiedManifestsBatch);
|
||||
});
|
||||
|
||||
this.logger.debug(`Deleted ${unifiedManifestsToDelete.length} unified manifests`);
|
||||
}
|
||||
|
||||
if (
|
||||
unifiedManifestsToCreate.length ||
|
||||
unifiedManifestsToUpdate.length ||
|
||||
unifiedManifestsToDelete.length
|
||||
) {
|
||||
// If global manifest is not in the list of manifests to create or update, we need to bump its version
|
||||
// We use it to set schemaVersion of the legacy manifest we are going to create so that its being picked up when populating agent policy
|
||||
const hasGlobalManifest = [...unifiedManifestsToCreate, ...unifiedManifestsToUpdate].some(
|
||||
(manifest) => manifest.policyId === '.global'
|
||||
);
|
||||
|
||||
if (!hasGlobalManifest || unifiedManifestsToDelete.length) {
|
||||
await this.bumpGlobalUnifiedManifestVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,8 +73,7 @@ describe('unified_manifest_client', () => {
|
|||
test('can get unified manifest by id', async () => {
|
||||
await unifiedManifestClient.getUnifiedManifestById('123');
|
||||
expect(savedObjectsClient.bulkGet).toHaveBeenCalledWith(
|
||||
expect.arrayContaining([mockSoClientCallParams({ id: '123' }, false)]),
|
||||
{ namespace: UNIFIED_MANIFEST_ALL_NAMESPACES }
|
||||
expect.arrayContaining([mockSoClientCallParams({ id: '123' }, false)])
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -84,8 +83,7 @@ describe('unified_manifest_client', () => {
|
|||
expect.arrayContaining([
|
||||
mockSoClientCallParams({ id: '123' }, false),
|
||||
mockSoClientCallParams({ id: '456' }, false),
|
||||
]),
|
||||
{ namespace: UNIFIED_MANIFEST_ALL_NAMESPACES }
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -114,17 +112,14 @@ describe('unified_manifest_client', () => {
|
|||
...mockUnifiedManifestAttributes({ policyId: `policy-${i}` }),
|
||||
id: `id-${i}`,
|
||||
created: '1',
|
||||
version: '1',
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
const cbFunc = jest.fn();
|
||||
await unifiedManifestClient.getAllUnifiedManifests(cbFunc);
|
||||
const result = await unifiedManifestClient.getAllUnifiedManifests();
|
||||
|
||||
expect(cbFunc).toHaveBeenCalledTimes(3);
|
||||
expect(cbFunc).toHaveBeenLastCalledWith([
|
||||
expect.objectContaining({ policyId: 'policy-2000', id: 'id-2000' }),
|
||||
]);
|
||||
expect(result.length).toBe(2001);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -140,8 +135,7 @@ describe('unified_manifest_client', () => {
|
|||
expect(savedObjectsClient.bulkUpdate).toHaveBeenCalledWith(
|
||||
expect.arrayContaining([
|
||||
mockSoClientCallParams({ id: '1234', version: 'abcd' }, true, false),
|
||||
]),
|
||||
{ namespace: UNIFIED_MANIFEST_ALL_NAMESPACES }
|
||||
])
|
||||
);
|
||||
});
|
||||
test('can update unified manifests', async () => {
|
||||
|
@ -153,8 +147,7 @@ describe('unified_manifest_client', () => {
|
|||
expect.arrayContaining([
|
||||
mockSoClientCallParams({ id: '1234', version: 'abcd' }, true, false),
|
||||
mockSoClientCallParams({ id: '1234', version: 'abcd' }, true, false),
|
||||
]),
|
||||
{ namespace: UNIFIED_MANIFEST_ALL_NAMESPACES }
|
||||
])
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -162,8 +155,7 @@ describe('unified_manifest_client', () => {
|
|||
test('can delete unified manifest', async () => {
|
||||
await unifiedManifestClient.deleteUnifiedManifestById('123');
|
||||
expect(savedObjectsClient.bulkDelete).toHaveBeenCalledWith(
|
||||
expect.arrayContaining([{ id: '123', type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE }]),
|
||||
{ namespace: UNIFIED_MANIFEST_ALL_NAMESPACES }
|
||||
expect.arrayContaining([{ id: '123', type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE }])
|
||||
);
|
||||
});
|
||||
test('can delete unified manifests', async () => {
|
||||
|
@ -172,8 +164,7 @@ describe('unified_manifest_client', () => {
|
|||
expect.arrayContaining([
|
||||
mockSoClientCallParams({ id: '123' }, false),
|
||||
mockSoClientCallParams({ id: '456' }, false),
|
||||
]),
|
||||
{ namespace: UNIFIED_MANIFEST_ALL_NAMESPACES }
|
||||
])
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -86,20 +86,19 @@ export class UnifiedManifestClient {
|
|||
manifestIds: string[]
|
||||
): Promise<SavedObjectsBulkResponse<InternalUnifiedManifestSchema>> {
|
||||
return this.savedObjectsClient.bulkGet(
|
||||
manifestIds.map((id) => ({ id, type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE })),
|
||||
{ namespace: UNIFIED_MANIFEST_ALL_NAMESPACES }
|
||||
manifestIds.map((id) => ({ id, type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE }))
|
||||
);
|
||||
}
|
||||
|
||||
public async getAllUnifiedManifests(
|
||||
cb: (unifiedManifests: InternalUnifiedManifestSchema[]) => void | Promise<void>,
|
||||
options?: FetchAllUnifiedManifestsOptions
|
||||
): Promise<void> {
|
||||
): Promise<InternalUnifiedManifestSchema[]> {
|
||||
const unifiedManifestsFetcher = this.fetchAllUnifiedManifests(this.savedObjectsClient, options);
|
||||
|
||||
const allUnifiedManifests: InternalUnifiedManifestSchema[] = [];
|
||||
for await (const unifiedManifests of unifiedManifestsFetcher) {
|
||||
await cb(unifiedManifests);
|
||||
allUnifiedManifests.push(...unifiedManifests);
|
||||
}
|
||||
return allUnifiedManifests;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -127,8 +126,7 @@ export class UnifiedManifestClient {
|
|||
attributes,
|
||||
...(version ? { version } : {}),
|
||||
};
|
||||
}),
|
||||
{ namespace: UNIFIED_MANIFEST_ALL_NAMESPACES }
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -144,8 +142,7 @@ export class UnifiedManifestClient {
|
|||
manifestIds: string[]
|
||||
): Promise<SavedObjectsBulkDeleteResponse> {
|
||||
return this.savedObjectsClient.bulkDelete(
|
||||
manifestIds.map((id) => ({ id, type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE })),
|
||||
{ namespace: UNIFIED_MANIFEST_ALL_NAMESPACES }
|
||||
manifestIds.map((id) => ({ id, type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE }))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -160,7 +157,7 @@ export class UnifiedManifestClient {
|
|||
fields = [],
|
||||
kuery,
|
||||
sortOrder = 'asc',
|
||||
sortField = 'created',
|
||||
sortField = 'created_at',
|
||||
}: FetchAllUnifiedManifestsOptions = {}
|
||||
): AsyncIterable<InternalUnifiedManifestSchema[]> {
|
||||
return createSoFindIterable<InternalUnifiedManifestBaseSchema>({
|
||||
|
|
|
@ -16,6 +16,7 @@ export const mapUnifiedManifestSavedObjectToUnifiedManifest = ({
|
|||
attributes: { artifactIds, policyId, semanticVersion },
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
created_at,
|
||||
version,
|
||||
}: SavedObject<InternalUnifiedManifestBaseSchema>): InternalUnifiedManifestSchema => {
|
||||
return {
|
||||
id,
|
||||
|
@ -23,5 +24,6 @@ export const mapUnifiedManifestSavedObjectToUnifiedManifest = ({
|
|||
semanticVersion,
|
||||
created: created_at,
|
||||
artifactIds,
|
||||
version,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -13,7 +13,7 @@ import { noteType, pinnedEventType, timelineType } from './lib/timeline/saved_ob
|
|||
import { legacyType as legacyRuleActionsType } from './lib/detection_engine/rule_actions_legacy';
|
||||
import { prebuiltRuleAssetType } from './lib/detection_engine/prebuilt_rules';
|
||||
import { type as signalsMigrationType } from './lib/detection_engine/migrations/saved_objects';
|
||||
import { manifestType } from './endpoint/lib/artifacts/saved_object_mappings';
|
||||
import { manifestType, unifiedManifestType } from './endpoint/lib/artifacts/saved_object_mappings';
|
||||
import { riskEngineConfigurationType } from './lib/entity_analytics/risk_engine/saved_object';
|
||||
|
||||
const types = [
|
||||
|
@ -23,6 +23,7 @@ const types = [
|
|||
prebuiltRuleAssetType,
|
||||
timelineType,
|
||||
manifestType,
|
||||
unifiedManifestType,
|
||||
signalsMigrationType,
|
||||
riskEngineConfigurationType,
|
||||
protectionUpdatesNoteType,
|
||||
|
|
|
@ -30,7 +30,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
|||
const pageObjects = getPageObjects(['common', 'artifactEntriesList']);
|
||||
const testSubjects = getService('testSubjects');
|
||||
const browser = getService('browser');
|
||||
const endpointArtifactsTestResources = getService('endpointArtifactTestResources');
|
||||
const endpointArtifactTestResources = getService('endpointArtifactTestResources');
|
||||
const endpointTestResources = getService('endpointTestResources');
|
||||
const retry = getService('retry');
|
||||
const esClient = getService('es');
|
||||
|
@ -76,7 +76,7 @@ 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 endpointArtifactsTestResources.getArtifacts();
|
||||
const artifacts = await endpointArtifactTestResources.getArtifacts();
|
||||
|
||||
const manifestArtifact = artifacts.find((artifact) => {
|
||||
return (
|
||||
|
|
|
@ -0,0 +1,376 @@
|
|||
/*
|
||||
* 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 '../../ftr_provider_context';
|
||||
import {
|
||||
ArtifactBodyType,
|
||||
getArtifactsListTestsData,
|
||||
ArtifactActionsType,
|
||||
AgentPolicyResponseType,
|
||||
getCreateMultipleData,
|
||||
MultipleArtifactActionsType,
|
||||
} from './mocks';
|
||||
import { PolicyTestResourceInfo } from '../../services/endpoint_policy';
|
||||
import { targetTags } from '../../target_tags';
|
||||
|
||||
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');
|
||||
};
|
||||
|
||||
describe('For each artifact list under management', function () {
|
||||
targetTags(this, ['@ess', '@serverless']);
|
||||
|
||||
this.timeout(60_000 * 5);
|
||||
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
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
* 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 '../../ftr_provider_context';
|
||||
import { targetTags } from '../../target_tags';
|
||||
|
||||
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');
|
||||
|
||||
describe('Endpoint Exceptions', function () {
|
||||
targetTags(this, ['@ess', '@serverless']);
|
||||
|
||||
this.timeout(10 * 60_000);
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
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',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
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',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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 '../../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'));
|
||||
});
|
||||
}
|
|
@ -0,0 +1,807 @@
|
|||
/*
|
||||
* 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',
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
});
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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,
|
||||
});
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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,
|
||||
});
|
||||
}
|
|
@ -6,9 +6,9 @@
|
|||
*/
|
||||
|
||||
import type {
|
||||
ExceptionListItemSchema,
|
||||
CreateExceptionListSchema,
|
||||
CreateExceptionListItemSchema,
|
||||
CreateExceptionListSchema,
|
||||
ExceptionListItemSchema,
|
||||
} from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { EXCEPTION_LIST_ITEM_URL, EXCEPTION_LIST_URL } from '@kbn/securitysolution-list-constants';
|
||||
import { Response } from 'superagent';
|
||||
|
@ -19,8 +19,10 @@ import { EVENT_FILTER_LIST_DEFINITION } from '@kbn/security-solution-plugin/publ
|
|||
import { HOST_ISOLATION_EXCEPTIONS_LIST_DEFINITION } from '@kbn/security-solution-plugin/public/management/pages/host_isolation_exceptions/constants';
|
||||
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';
|
||||
|
||||
export interface ArtifactTestData {
|
||||
artifact: ExceptionListItemSchema;
|
||||
|
@ -132,8 +134,25 @@ export class EndpointArtifactsTestResources extends FtrService {
|
|||
});
|
||||
|
||||
const manifestResult = manifestResults[0] as InternalManifestSchemaResponseType;
|
||||
const artifacts = manifestResult._source['endpoint:user-artifact-manifest'].artifacts;
|
||||
return manifestResult._source['endpoint:user-artifact-manifest'].artifacts;
|
||||
}
|
||||
|
||||
return artifacts;
|
||||
async getArtifactsFromUnifiedManifestSO(): Promise<
|
||||
Array<
|
||||
InternalUnifiedManifestSchemaResponseType['_source']['endpoint:unified-user-artifact-manifest']
|
||||
>
|
||||
> {
|
||||
const {
|
||||
hits: { hits: manifestResults },
|
||||
} = await this.esClient.search<InternalUnifiedManifestSchemaResponseType['_source']>({
|
||||
index: '.kibana*',
|
||||
query: {
|
||||
bool: { filter: [{ term: { type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE } }] },
|
||||
},
|
||||
});
|
||||
|
||||
return manifestResults.map(
|
||||
(result) => result._source!['endpoint:unified-user-artifact-manifest']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue