diff --git a/x-pack/solutions/security/test/security_solution_api_integration/config/ess/config.base.ts b/x-pack/solutions/security/test/security_solution_api_integration/config/ess/config.base.ts index 8faa12258c7bb..2391fe4344c0b 100644 --- a/x-pack/solutions/security/test/security_solution_api_integration/config/ess/config.base.ts +++ b/x-pack/solutions/security/test/security_solution_api_integration/config/ess/config.base.ts @@ -110,7 +110,6 @@ export function createTestConfig(options: CreateTestConfigOptions, testFiles?: s 'previewTelemetryUrlEnabled', 'endpointExceptionsMovedUnderManagement', 'ruleChangesHistoryEnabled', - 'disable:entityAnalyticsEntityStoreV2', ])}`, '--xpack.alerting.ruleChangeTracking.enabled=true', `--plugin-path=${path.resolve( diff --git a/x-pack/solutions/security/test/security_solution_api_integration/config/serverless/config.base.ts b/x-pack/solutions/security/test/security_solution_api_integration/config/serverless/config.base.ts index e1ce83ff6c9f1..6ad880089556c 100644 --- a/x-pack/solutions/security/test/security_solution_api_integration/config/serverless/config.base.ts +++ b/x-pack/solutions/security/test/security_solution_api_integration/config/serverless/config.base.ts @@ -49,7 +49,6 @@ export function createTestConfig(options: CreateTestConfigOptions) { `--xpack.securitySolution.enableExperimental=${JSON.stringify([ 'endpointExceptionsMovedUnderManagement', 'ruleChangesHistoryEnabled', - 'disable:entityAnalyticsEntityStoreV2', ])}`, '--xpack.alerting.ruleChangeTracking.enabled=true', ...(options.kbnTestServerArgs || []), diff --git a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/entity_store_v2_enrichment_setup.ts b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/entity_store_v2_enrichment_setup.ts new file mode 100644 index 0000000000000..2cbcb899523e2 --- /dev/null +++ b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/entity_store_v2_enrichment_setup.ts @@ -0,0 +1,143 @@ +/* + * 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 type { FtrProviderContext } from '../../../../ftr_provider_context'; + +const PUBLIC_API_HEADERS: ReadonlyArray<[string, string]> = [ + ['kbn-xsrf', 'true'], + ['x-elastic-internal-origin', 'Kibana'], + ['elastic-api-version', '2023-10-31'], +]; + +const withHeaders = T }>(req: T): T => + PUBLIC_API_HEADERS.reduce((acc, [k, v]) => acc.set(k, v), req); + +export interface HostEntityConfig { + host: Record; + entity?: Record; + asset?: Record; +} + +export interface UserEntityConfig { + user: Record; + host?: Record; + entity?: Record; + asset?: Record; +} + +export interface EnrichmentSetupConfig { + hosts?: HostEntityConfig[]; + users?: UserEntityConfig[]; +} + +/** + * Installs Entity Store V2 engines and seeds entities for alert enrichment tests. + * + * Entity creation uses `refresh: 'wait_for'` internally, so seeded entities are + * immediately searchable when `setup` returns. No additional refresh step is needed. + * + * Inspired by the approach started in https://github.com/elastic/kibana/pull/270939 + * by @denar50. + */ +export const EntityStoreV2EnrichmentSetup = (getService: FtrProviderContext['getService']) => { + const supertest = getService('supertest'); + const retry = getService('retry'); + const log = getService('log'); + + /** + * Installs Entity Store V2 engines and seeds entities. + * + * Returns `false` when Entity Store V2 is not available in the current environment + * (e.g. ESS / non-MKI stateful). Callers should call `this.skip()` when `false` is + * returned so the test suite is skipped rather than failing. + * + * Returns `true` when setup completed successfully. + */ + const setup = async (enrichmentConfig: EnrichmentSetupConfig): Promise => { + const entityTypes: string[] = []; + if (enrichmentConfig.hosts?.length) entityTypes.push('host'); + if (enrichmentConfig.users?.length) entityTypes.push('user'); + + if (entityTypes.length === 0) return true; + + const installRes = await withHeaders(supertest.post('/api/security/entity_store/install')).send( + { entityTypes } + ); + + if (installRes.status !== 200 && installRes.status !== 201) { + log.debug( + `Entity Store V2 is not available in this environment (status ${installRes.status}), skipping` + ); + return false; + } + + // Wait until the initial entity store maintainer scan completes (`stopped`) before seeding + // test entities. Seeding during a `running` scan risks having the engine overwrite the test + // data before the detection rule runs. If the scan does not complete within the timeout the + // environment is not suitable for enrichment tests, so we skip gracefully. + const scanCompleted = await retry + .waitForWithTimeout('Entity Store V2 initial scan to complete', 120_000, async () => { + const res = await withHeaders(supertest.get('/api/security/entity_store/status')); + if (res.body.status === 'error') { + throw new Error(`Entity Store V2 install errored: ${JSON.stringify(res.body)}`); + } + return res.body.status === 'stopped'; + }) + .then(() => true) + .catch((err: Error) => { + log.debug(`Entity Store V2 scan did not complete in time, skipping: ${err.message}`); + return false; + }); + + if (!scanCompleted) { + return false; + } + + for (const hostConfig of enrichmentConfig.hosts ?? []) { + const createRes = await withHeaders( + supertest.post('/api/security/entity_store/entities/host') + ).send(hostConfig); + if (createRes.status !== 200) { + throw new Error( + `Create host entity failed (status ${createRes.status}): ${JSON.stringify( + createRes.body + )}` + ); + } + } + + for (const userConfig of enrichmentConfig.users ?? []) { + const createRes = await withHeaders( + supertest.post('/api/security/entity_store/entities/user') + ).send(userConfig); + if (createRes.status !== 200) { + throw new Error( + `Create user entity failed (status ${createRes.status}): ${JSON.stringify( + createRes.body + )}` + ); + } + } + + return true; + }; + + const teardown = async (): Promise => { + const res = await withHeaders(supertest.post('/api/security/entity_store/uninstall')).send({ + entityTypes: ['user', 'host', 'service'], + }); + if (res.status !== 200) { + log.debug( + `Entity Store V2 uninstall returned non-200 (status ${res.status}): ${JSON.stringify( + res.body + )}` + ); + } + }; + + return { setup, teardown }; +}; diff --git a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/eql/trial_license_complete_tier/eql.ts b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/eql/trial_license_complete_tier/eql.ts index fb8ecde656a90..92ca709c22bb3 100644 --- a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/eql/trial_license_complete_tier/eql.ts +++ b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/eql/trial_license_complete_tier/eql.ts @@ -63,6 +63,7 @@ import { import type { FtrProviderContext } from '../../../../../../ftr_provider_context'; import { EsArchivePathBuilder } from '../../../../../../es_archive_path_builder'; import { getMetricsRequest, getMetricsWithRetry } from '../../utils'; +import { EntityStoreV2EnrichmentSetup } from '../../entity_store_v2_enrichment_setup'; /** * Specific AGENT_ID to use for some of the tests. If the archiver changes and you see errors @@ -71,12 +72,19 @@ import { getMetricsRequest, getMetricsWithRetry } from '../../utils'; const AGENT_ID = 'a1d7b39c-f898-4dbe-a761-efb61939302d'; const specificQueryForTests = `configuration where agent.id=="${AGENT_ID}"`; +// host.id of the auditbeat record matched by `specificQueryForTests`. +// EUID priority is host.id, so the entity EUID is `host:${ENRICHMENT_HOST_ID}`. +const ENRICHMENT_HOST_ID = '8cc95778cce5407c809480e8e32ad76b'; +const ENRICHMENT_HOST_NAME = 'suricata-zeek-sensor-toronto'; +const ENRICHMENT_HOST_EUID = `host:${ENRICHMENT_HOST_ID}`; + export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); const es = getService('es'); const log = getService('log'); const retry = getService('retry'); + const entityStoreV2 = EntityStoreV2EnrichmentSetup(getService); // TODO: add a new service for loading archiver files similar to "getService('es')" const config = getService('config'); @@ -783,15 +791,29 @@ export default ({ getService }: FtrProviderContext) => { }); describe('with host risk index', () => { - before(async () => { - await esArchiver.load('x-pack/solutions/security/test/fixtures/es_archives/entity/risks'); + before(async function () { + if ( + !(await entityStoreV2.setup({ + hosts: [ + { + host: { name: ENRICHMENT_HOST_NAME, id: [ENRICHMENT_HOST_ID] }, + entity: { + id: ENRICHMENT_HOST_EUID, + type: 'host', + risk: { calculated_level: 'Critical', calculated_score_norm: 96 }, + }, + }, + ], + })) + ) + return this.skip(); }); after(async () => { - await esArchiver.unload('x-pack/solutions/security/test/fixtures/es_archives/entity/risks'); + await entityStoreV2.teardown(); }); - it('@skipInServerlessMKI should be enriched with host risk score', async () => { + it('should be enriched with host risk score', async () => { const rule: EqlRuleCreateProps = { ...getEqlRuleForAlertTesting(['auditbeat-*']), query: specificQueryForTests, @@ -809,19 +831,26 @@ export default ({ getService }: FtrProviderContext) => { }); describe('with asset criticality', () => { - before(async () => { - await esArchiver.load( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + before(async function () { + if ( + !(await entityStoreV2.setup({ + hosts: [ + { + host: { name: ENRICHMENT_HOST_NAME, id: [ENRICHMENT_HOST_ID] }, + entity: { id: ENRICHMENT_HOST_EUID, type: 'host' }, + asset: { criticality: 'high_impact' }, + }, + ], + })) + ) + return this.skip(); }); after(async () => { - await esArchiver.unload( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + await entityStoreV2.teardown(); }); - it('@skipInServerlessMKI should be enriched alert with criticality_level', async () => { + it('should be enriched alert with criticality_level', async () => { const rule: EqlRuleCreateProps = { ...getEqlRuleForAlertTesting(['auditbeat-*']), query: specificQueryForTests, diff --git a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/eql/trial_license_complete_tier/eql_alert_suppression.ts b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/eql/trial_license_complete_tier/eql_alert_suppression.ts index 916500d859c95..d3f2a833f4b7d 100644 --- a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/eql/trial_license_complete_tier/eql_alert_suppression.ts +++ b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/eql/trial_license_complete_tier/eql_alert_suppression.ts @@ -47,6 +47,7 @@ import { } from '../../../../utils'; import type { FtrProviderContext } from '../../../../../../ftr_provider_context'; import { deleteAllExceptions } from '../../../../../lists_and_exception_lists/utils'; +import { EntityStoreV2EnrichmentSetup } from '../../entity_store_v2_enrichment_setup'; const getQuery = (id: string) => `any where id == "${id}"`; const getSequenceQuery = (id: string) => @@ -56,6 +57,7 @@ export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); const esDeleteAllIndices = getService('esDeleteAllIndices'); + const entityStoreV2 = EntityStoreV2EnrichmentSetup(getService); const es = getService('es'); const log = getService('log'); @@ -1718,23 +1720,43 @@ export default ({ getService }: FtrProviderContext) => { }); describe('alert enrichment', () => { - before(async () => { - await esArchiver.load('x-pack/solutions/security/test/fixtures/es_archives/entity/risks'); - await esArchiver.load( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + before(async function () { + // Dynamic docs in this describe only have host.name / user.name (no host.id), + // so the EUID is name-based. + if ( + !(await entityStoreV2.setup({ + hosts: [ + { + host: { name: 'suricata-zeek-sensor-toronto' }, + entity: { + id: 'host:suricata-zeek-sensor-toronto', + type: 'host', + risk: { calculated_level: 'Critical', calculated_score_norm: 96 }, + }, + }, + { + host: { name: 'zeek-newyork-sha-aa8df15' }, + entity: { id: 'host:zeek-newyork-sha-aa8df15', type: 'host' }, + asset: { criticality: 'medium_impact' }, + }, + ], + users: [ + { + user: { name: 'root' }, + entity: { id: 'user:root@unknown', type: 'user' }, + asset: { criticality: 'extreme_impact' }, + }, + ], + })) + ) + return this.skip(); }); after(async () => { - await esArchiver.unload( - 'x-pack/solutions/security/test/fixtures/es_archives/entity/risks' - ); - await esArchiver.unload( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + await entityStoreV2.teardown(); }); - it('@skipInServerlessMKI suppressed alerts are enriched with host risk score', async () => { + it('suppressed alerts are enriched with host risk score', async () => { const eventId = uuidv4(); await indexGeneratedSourceDocuments({ docsCount: 1, @@ -1763,7 +1785,7 @@ export default ({ getService }: FtrProviderContext) => { expect(previewAlerts[0]?._source?.host?.risk?.calculated_score_norm).toBe(96); }); - it('@skipInServerlessMKI suppressed alerts are enriched with criticality_level', async () => { + it('suppressed alerts are enriched with criticality_level', async () => { const id = uuidv4(); const timestamp = '2020-10-28T06:45:00.000Z'; diff --git a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/esql/trial_license_complete_tier/esql.ts b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/esql/trial_license_complete_tier/esql.ts index c06d3d00374f0..a5ecdff6f2667 100644 --- a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/esql/trial_license_complete_tier/esql.ts +++ b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/esql/trial_license_complete_tier/esql.ts @@ -50,6 +50,7 @@ import { import { deleteAllExceptions } from '../../../../../lists_and_exception_lists/utils'; import type { FtrProviderContext } from '../../../../../../ftr_provider_context'; import { EsArchivePathBuilder } from '../../../../../../es_archive_path_builder'; +import { EntityStoreV2EnrichmentSetup } from '../../entity_store_v2_enrichment_setup'; export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); @@ -57,6 +58,7 @@ export default ({ getService }: FtrProviderContext) => { const es = getService('es'); const log = getService('log'); const utils = getService('securitySolutionUtils'); + const entityStoreV2 = EntityStoreV2EnrichmentSetup(getService); const { indexEnhancedDocuments, indexListOfDocuments, indexGeneratedDocuments } = dataGeneratorFactory({ @@ -1972,15 +1974,29 @@ export default ({ getService }: FtrProviderContext) => { }); describe('alerts enrichment', () => { - before(async () => { - await esArchiver.load('x-pack/solutions/security/test/fixtures/es_archives/entity/risks'); + before(async function () { + if ( + !(await entityStoreV2.setup({ + hosts: [ + { + host: { name: 'host-0' }, + entity: { + id: 'host:host-0', + type: 'host', + risk: { calculated_level: 'Low', calculated_score_norm: 1 }, + }, + }, + ], + })) + ) + return this.skip(); }); after(async () => { - await esArchiver.unload('x-pack/solutions/security/test/fixtures/es_archives/entity/risks'); + await entityStoreV2.teardown(); }); - it('@skipInServerlessMKI should be enriched with host risk score', async () => { + it('should be enriched with host risk score', async () => { const id = uuidv4(); const interval: [string, string] = ['2020-10-28T06:00:00.000Z', '2020-10-28T06:10:00.000Z']; const doc1 = { host: { name: 'host-0' } }; @@ -2010,19 +2026,26 @@ export default ({ getService }: FtrProviderContext) => { }); describe('with asset criticality', () => { - before(async () => { - await esArchiver.load( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + before(async function () { + if ( + !(await entityStoreV2.setup({ + hosts: [ + { + host: { name: 'host-0' }, + entity: { id: 'host:host-0', type: 'host' }, + asset: { criticality: 'extreme_impact' }, + }, + ], + })) + ) + return this.skip(); }); after(async () => { - await esArchiver.unload( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + await entityStoreV2.teardown(); }); - it('@skipInServerlessMKI should be enriched alert with criticality_level', async () => { + it('should be enriched alert with criticality_level', async () => { const id = uuidv4(); const interval: [string, string] = ['2020-10-28T06:00:00.000Z', '2020-10-28T06:10:00.000Z']; const doc1 = { host: { name: 'host-0' } }; diff --git a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/esql/trial_license_complete_tier/esql_suppression.ts b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/esql/trial_license_complete_tier/esql_suppression.ts index aac7567ff15e4..7f0db044576a3 100644 --- a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/esql/trial_license_complete_tier/esql_suppression.ts +++ b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/esql/trial_license_complete_tier/esql_suppression.ts @@ -37,10 +37,12 @@ import { } from '../../../../utils'; import { deleteAllExceptions } from '../../../../../lists_and_exception_lists/utils'; import type { FtrProviderContext } from '../../../../../../ftr_provider_context'; +import { EntityStoreV2EnrichmentSetup } from '../../entity_store_v2_enrichment_setup'; export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); + const entityStoreV2 = EntityStoreV2EnrichmentSetup(getService); const es = getService('es'); const log = getService('log'); const { indexEnhancedDocuments, indexListOfDocuments, indexGeneratedDocuments } = @@ -2024,15 +2026,29 @@ export default ({ getService }: FtrProviderContext) => { }); describe('alerts enrichment', () => { - before(async () => { - await esArchiver.load('x-pack/solutions/security/test/fixtures/es_archives/entity/risks'); + before(async function () { + if ( + !(await entityStoreV2.setup({ + hosts: [ + { + host: { name: 'host-0' }, + entity: { + id: 'host:host-0', + type: 'host', + risk: { calculated_level: 'Low', calculated_score_norm: 1 }, + }, + }, + ], + })) + ) + return this.skip(); }); after(async () => { - await esArchiver.unload('x-pack/solutions/security/test/fixtures/es_archives/entity/risks'); + await entityStoreV2.teardown(); }); - it('@skipInServerlessMKI should be enriched with host risk score', async () => { + it('should be enriched with host risk score', async () => { const id = uuidv4(); const interval: [string, string] = ['2020-10-28T06:00:00.000Z', '2020-10-28T06:10:00.000Z']; const doc1 = { host: { name: 'host-0' } }; @@ -2066,19 +2082,26 @@ export default ({ getService }: FtrProviderContext) => { }); describe('with asset criticality', () => { - before(async () => { - await esArchiver.load( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + before(async function () { + if ( + !(await entityStoreV2.setup({ + hosts: [ + { + host: { name: 'host-0' }, + entity: { id: 'host:host-0', type: 'host' }, + asset: { criticality: 'extreme_impact' }, + }, + ], + })) + ) + return this.skip(); }); after(async () => { - await esArchiver.unload( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + await entityStoreV2.teardown(); }); - it('@skipInServerlessMKI should be enriched alert with criticality_level', async () => { + it('should be enriched alert with criticality_level', async () => { const id = uuidv4(); const interval: [string, string] = ['2020-10-28T06:00:00.000Z', '2020-10-28T06:10:00.000Z']; const doc1 = { host: { name: 'host-0' } }; diff --git a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/indicator_match/trial_license_complete_tier/indicator_match.ts b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/indicator_match/trial_license_complete_tier/indicator_match.ts index 712a34cf5239f..ea0f1f3233748 100644 --- a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/indicator_match/trial_license_complete_tier/indicator_match.ts +++ b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/indicator_match/trial_license_complete_tier/indicator_match.ts @@ -57,6 +57,13 @@ import { } from '../../../../utils'; import type { FtrProviderContext } from '../../../../../../ftr_provider_context'; import { EsArchivePathBuilder } from '../../../../../../es_archive_path_builder'; +import { EntityStoreV2EnrichmentSetup } from '../../entity_store_v2_enrichment_setup'; + +// Entity fields for the auditbeat record with ancestor _id = '7yJ-B2kBR346wHgnhlMn'. +const ENRICHMENT_HOST_ID = '2ce8b1e7d69e4a1d9c6bcddc473da9d9'; +const ENRICHMENT_HOST_NAME = 'zeek-sensor-amsterdam'; +const ENRICHMENT_HOST_EUID = `host:${ENRICHMENT_HOST_ID}`; +const ENRICHMENT_USER_NAME = 'root'; const createThreatMatchRule = ({ name = 'Query with a rule id', @@ -171,6 +178,7 @@ export default ({ getService }: FtrProviderContext) => { const config = getService('config'); const isServerless = config.get('serverless'); const utils = getService('securitySolutionUtils'); + const entityStoreV2 = EntityStoreV2EnrichmentSetup(getService); const dataPathBuilder = new EsArchivePathBuilder(isServerless); const audibeatHostsPath = dataPathBuilder.getPath('auditbeat/hosts'); const threatIntelPath = dataPathBuilder.getPath('filebeat/threat_intel'); @@ -2459,15 +2467,29 @@ export default ({ getService }: FtrProviderContext) => { }); describe('alerts should be enriched', () => { - before(async () => { - await esArchiver.load('x-pack/solutions/security/test/fixtures/es_archives/entity/risks'); + before(async function () { + if ( + !(await entityStoreV2.setup({ + hosts: [ + { + host: { name: ENRICHMENT_HOST_NAME, id: [ENRICHMENT_HOST_ID] }, + entity: { + id: ENRICHMENT_HOST_EUID, + type: 'host', + risk: { calculated_level: 'Critical', calculated_score_norm: 70 }, + }, + }, + ], + })) + ) + return this.skip(); }); after(async () => { - await esArchiver.unload('x-pack/solutions/security/test/fixtures/es_archives/entity/risks'); + await entityStoreV2.teardown(); }); - it('@skipInServerlessMKI should be enriched with host risk score', async () => { + it('should be enriched with host risk score', async () => { const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({ query: '*:*', threat_query: 'source.ip: "188.166.120.93"', // narrow things down with a query to a specific source ip @@ -2503,19 +2525,39 @@ export default ({ getService }: FtrProviderContext) => { }); describe('with asset criticality', () => { - before(async () => { - await esArchiver.load( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + before(async function () { + if ( + !(await entityStoreV2.setup({ + hosts: [ + { + host: { name: ENRICHMENT_HOST_NAME, id: [ENRICHMENT_HOST_ID] }, + entity: { id: ENRICHMENT_HOST_EUID, type: 'host' }, + asset: { criticality: 'low_impact' }, + }, + ], + users: [ + { + // Indicator match alerts come from auditbeat events that carry host.id, + // triggering the local-namespace user EUID: user:@@local. + user: { name: ENRICHMENT_USER_NAME }, + host: { id: [ENRICHMENT_HOST_ID] }, + entity: { + id: `user:${ENRICHMENT_USER_NAME}@${ENRICHMENT_HOST_ID}@local`, + type: 'user', + }, + asset: { criticality: 'extreme_impact' }, + }, + ], + })) + ) + return this.skip(); }); after(async () => { - await esArchiver.unload( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + await entityStoreV2.teardown(); }); - it('@skipInServerlessMKI should be enriched alert with criticality_level', async () => { + it('should be enriched alert with criticality_level', async () => { const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({ query: '*:*', threat_query: 'source.ip: "188.166.120.93"', // narrow things down with a query to a specific source ip diff --git a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/indicator_match/trial_license_complete_tier/indicator_match_alert_suppression.ts b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/indicator_match/trial_license_complete_tier/indicator_match_alert_suppression.ts index d82d745220ed3..9f650947577d6 100644 --- a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/indicator_match/trial_license_complete_tier/indicator_match_alert_suppression.ts +++ b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/indicator_match/trial_license_complete_tier/indicator_match_alert_suppression.ts @@ -37,12 +37,14 @@ import { dataGeneratorFactory, } from '../../../../utils'; import type { FtrProviderContext } from '../../../../../../ftr_provider_context'; +import { EntityStoreV2EnrichmentSetup } from '../../entity_store_v2_enrichment_setup'; export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); const es = getService('es'); const log = getService('log'); + const entityStoreV2 = EntityStoreV2EnrichmentSetup(getService); const { indexListOfDocuments: indexListOfSourceDocuments, @@ -2497,17 +2499,37 @@ export default ({ getService }: FtrProviderContext) => { }); }); - describe('@skipInServerlessMKI alerts should be enriched', () => { - before(async () => { - await esArchiver.load( - 'x-pack/solutions/security/test/fixtures/es_archives/entity/risks' - ); + describe('alerts should be enriched', () => { + before(async function () { + if ( + !(await entityStoreV2.setup({ + hosts: [ + { + host: { name: 'zeek-sensor-amsterdam' }, + entity: { + id: 'host:zeek-sensor-amsterdam', + type: 'host', + risk: { calculated_level: 'Critical', calculated_score_norm: 70 }, + }, + }, + ], + users: [ + { + user: { name: 'root' }, + entity: { + id: 'user:root@unknown', + type: 'user', + risk: { calculated_level: 'Low', calculated_score_norm: 11 }, + }, + }, + ], + })) + ) + return this.skip(); }); after(async () => { - await esArchiver.unload( - 'x-pack/solutions/security/test/fixtures/es_archives/entity/risks' - ); + await entityStoreV2.teardown(); }); it('should be enriched with host risk score', async () => { @@ -2570,17 +2592,31 @@ export default ({ getService }: FtrProviderContext) => { }); }); - describe('@skipInServerlessMKI with asset criticality', () => { - before(async () => { - await esArchiver.load( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + describe('with asset criticality', () => { + before(async function () { + if ( + !(await entityStoreV2.setup({ + hosts: [ + { + host: { name: 'zeek-sensor-amsterdam' }, + entity: { id: 'host:zeek-sensor-amsterdam', type: 'host' }, + asset: { criticality: 'low_impact' }, + }, + ], + users: [ + { + user: { name: 'root' }, + entity: { id: 'user:root@unknown', type: 'user' }, + asset: { criticality: 'extreme_impact' }, + }, + ], + })) + ) + return this.skip(); }); after(async () => { - await esArchiver.unload( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + await entityStoreV2.teardown(); }); it('should be enriched alert with criticality_level', async () => { diff --git a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/machine_learning/trial_license_complete_tier/machine_learning.ts b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/machine_learning/trial_license_complete_tier/machine_learning.ts index cbc33147b9f01..0eedb1b1644de 100644 --- a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/machine_learning/trial_license_complete_tier/machine_learning.ts +++ b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/machine_learning/trial_license_complete_tier/machine_learning.ts @@ -58,6 +58,7 @@ import { import type { FtrProviderContext } from '../../../../../../ftr_provider_context'; import { EsArchivePathBuilder } from '../../../../../../es_archive_path_builder'; import { getMetricsRequest, getMetricsWithRetry } from '../../utils'; +import { EntityStoreV2EnrichmentSetup } from '../../entity_store_v2_enrichment_setup'; export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); @@ -71,6 +72,7 @@ export default ({ getService }: FtrProviderContext) => { const dataPathBuilder = new EsArchivePathBuilder(isServerless); const auditPath = dataPathBuilder.getPath('auditbeat/hosts'); const retry = getService('retry'); + const entityStoreV2 = EntityStoreV2EnrichmentSetup(getService); const siemModule = 'security_linux_v3'; const mlJobId = 'v3_linux_anomalous_network_activity_ea'; @@ -312,16 +314,30 @@ export default ({ getService }: FtrProviderContext) => { }); }); - describe('alerts should be be enriched', () => { - before(async () => { - await esArchiver.load('x-pack/solutions/security/test/fixtures/es_archives/entity/risks'); + describe('alerts should be enriched', () => { + before(async function () { + if ( + !(await entityStoreV2.setup({ + hosts: [ + { + host: { name: 'mothra' }, + entity: { + id: 'host:mothra', + type: 'host', + risk: { calculated_level: 'Low', calculated_score_norm: 1 }, + }, + }, + ], + })) + ) + return this.skip(); }); after(async () => { - await esArchiver.unload('x-pack/solutions/security/test/fixtures/es_archives/entity/risks'); + await entityStoreV2.teardown(); }); - it('@skipInServerlessMKI should be enriched with host risk score', async () => { + it('should be enriched with host risk score', async () => { const { previewId } = await previewRule({ supertest, rule }); const previewAlerts = await getPreviewAlerts({ es, previewId }); expect(previewAlerts).toHaveLength(1); @@ -333,19 +349,29 @@ export default ({ getService }: FtrProviderContext) => { }); describe('with asset criticality', () => { - before(async () => { - await esArchiver.load( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + before(async function () { + if ( + !(await entityStoreV2.setup({ + users: [ + { + user: { name: 'root' }, + entity: { + id: 'user:root@unknown', + type: 'user', + }, + asset: { criticality: 'extreme_impact' }, + }, + ], + })) + ) + return this.skip(); }); after(async () => { - await esArchiver.unload( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + await entityStoreV2.teardown(); }); - it('@skipInServerlessMKI should be enriched alert with criticality_level', async () => { + it('should be enriched alert with criticality_level', async () => { const { previewId } = await previewRule({ supertest, rule }); const previewAlerts = await getPreviewAlerts({ es, previewId }); diff --git a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/new_terms/trial_license_complete_tier/new_terms.ts b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/new_terms/trial_license_complete_tier/new_terms.ts index e0dbed5d2240d..c3e83e8de9995 100644 --- a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/new_terms/trial_license_complete_tier/new_terms.ts +++ b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/new_terms/trial_license_complete_tier/new_terms.ts @@ -32,6 +32,12 @@ import { import { deleteAllExceptions } from '../../../../../lists_and_exception_lists/utils'; import type { FtrProviderContext } from '../../../../../../ftr_provider_context'; import { EsArchivePathBuilder } from '../../../../../../es_archive_path_builder'; +import { EntityStoreV2EnrichmentSetup } from '../../entity_store_v2_enrichment_setup'; + +// host.id of the auditbeat record for zeek-newyork-sha-aa8df15 (first new term). +const ENRICHMENT_HOST_ID = '3729d06ce9964aa98549f41cbd99334d'; +const ENRICHMENT_HOST_NAME = 'zeek-newyork-sha-aa8df15'; +const ENRICHMENT_HOST_EUID = `host:${ENRICHMENT_HOST_ID}`; const historicalWindowStart = '2022-10-13T05:00:04.000Z'; const ruleExecutionStart = '2022-10-19T05:00:04.000Z'; @@ -41,6 +47,7 @@ export default ({ getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); const es = getService('es'); const log = getService('log'); + const entityStoreV2 = EntityStoreV2EnrichmentSetup(getService); const { indexEnhancedDocuments } = dataGeneratorFactory({ es, index: 'new_terms', @@ -1040,15 +1047,30 @@ export default ({ getService }: FtrProviderContext) => { }); describe('alerts should be be enriched', () => { - before(async () => { - await esArchiver.load('x-pack/solutions/security/test/fixtures/es_archives/entity/risks'); + before(async function () { + // The first new term alert uses auditbeat data (host.id present), so EUID is id-based. + if ( + !(await entityStoreV2.setup({ + hosts: [ + { + host: { name: ENRICHMENT_HOST_NAME, id: [ENRICHMENT_HOST_ID] }, + entity: { + id: ENRICHMENT_HOST_EUID, + type: 'host', + risk: { calculated_level: 'Low', calculated_score_norm: 23 }, + }, + }, + ], + })) + ) + return this.skip(); }); after(async () => { - await esArchiver.unload('x-pack/solutions/security/test/fixtures/es_archives/entity/risks'); + await entityStoreV2.teardown(); }); - it('@skipInServerlessMKI should be enriched with host risk score', async () => { + it('should be enriched with host risk score', async () => { const rule: NewTermsRuleCreateProps = { ...getCreateNewTermsRulesSchemaMock('rule-1', true), new_terms_fields: ['host.name'], @@ -1065,22 +1087,37 @@ export default ({ getService }: FtrProviderContext) => { }); describe('with asset criticality', () => { - before(async () => { + before(async function () { await esArchiver.load( 'x-pack/solutions/security/test/fixtures/es_archives/security_solution/ecs_compliant' ); - await esArchiver.load( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + // Dynamic docs use host.name only (no host.id) → name-based EUID. + if ( + !(await entityStoreV2.setup({ + hosts: [ + { + host: { name: ENRICHMENT_HOST_NAME }, + entity: { id: `host:${ENRICHMENT_HOST_NAME}`, type: 'host' }, + asset: { criticality: 'medium_impact' }, + }, + ], + users: [ + { + user: { name: 'root' }, + entity: { id: 'user:root@unknown', type: 'user' }, + asset: { criticality: 'extreme_impact' }, + }, + ], + })) + ) + return this.skip(); }); after(async () => { await esArchiver.unload( 'x-pack/solutions/security/test/fixtures/es_archives/security_solution/ecs_compliant' ); - await esArchiver.unload( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + await entityStoreV2.teardown(); }); const { indexListOfDocuments } = dataGeneratorFactory({ @@ -1089,7 +1126,7 @@ export default ({ getService }: FtrProviderContext) => { log, }); - it('@skipInServerlessMKI should be enriched alert with criticality_level', async () => { + it('should be enriched alert with criticality_level', async () => { const id = uuidv4(); const timestamp = '2020-10-28T06:45:00.000Z'; diff --git a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/new_terms/trial_license_complete_tier/new_terms_alert_suppression.ts b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/new_terms/trial_license_complete_tier/new_terms_alert_suppression.ts index a86aa421ce8f2..eb3f15e835557 100644 --- a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/new_terms/trial_license_complete_tier/new_terms_alert_suppression.ts +++ b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/new_terms/trial_license_complete_tier/new_terms_alert_suppression.ts @@ -40,12 +40,19 @@ import { import type { FtrProviderContext } from '../../../../../../ftr_provider_context'; import { deleteAllExceptions } from '../../../../../lists_and_exception_lists/utils'; import { EsArchivePathBuilder } from '../../../../../../es_archive_path_builder'; +import { EntityStoreV2EnrichmentSetup } from '../../entity_store_v2_enrichment_setup'; + +// host.id of the auditbeat record for zeek-newyork-sha-aa8df15 (first new term). +const ENRICHMENT_HOST_ID = '3729d06ce9964aa98549f41cbd99334d'; +const ENRICHMENT_HOST_NAME = 'zeek-newyork-sha-aa8df15'; +const ENRICHMENT_HOST_EUID = `host:${ENRICHMENT_HOST_ID}`; export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); const es = getService('es'); const log = getService('log'); + const entityStoreV2 = EntityStoreV2EnrichmentSetup(getService); const { indexListOfDocuments, indexGeneratedDocuments } = dataGeneratorFactory({ es, @@ -2248,26 +2255,48 @@ export default ({ getService }: FtrProviderContext) => { }); }); - describe('@skipInServerlessMKI enrichment', () => { + describe('enrichment', () => { const config = getService('config'); const isServerless = config.get('serverless'); const dataPathBuilder = new EsArchivePathBuilder(isServerless); const path = dataPathBuilder.getPath('auditbeat/hosts'); - - before(async () => { - await esArchiver.load('x-pack/solutions/security/test/fixtures/es_archives/entity/risks'); + before(async function () { await esArchiver.load(path); - await esArchiver.load( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + // Two entities are needed: + // 1. Id-based EUID for auditbeat docs (risk test, host.id present in alert). + // 2. Name-based EUID for dynamic ecs_compliant docs (criticality test, no host.id). + if ( + !(await entityStoreV2.setup({ + hosts: [ + { + host: { name: ENRICHMENT_HOST_NAME, id: [ENRICHMENT_HOST_ID] }, + entity: { + id: ENRICHMENT_HOST_EUID, + type: 'host', + risk: { calculated_level: 'Low', calculated_score_norm: 23 }, + }, + }, + { + host: { name: ENRICHMENT_HOST_NAME }, + entity: { id: `host:${ENRICHMENT_HOST_NAME}`, type: 'host' }, + asset: { criticality: 'medium_impact' }, + }, + ], + users: [ + { + user: { name: 'root' }, + entity: { id: 'user:root@unknown', type: 'user' }, + asset: { criticality: 'extreme_impact' }, + }, + ], + })) + ) + return this.skip(); }); after(async () => { - await esArchiver.unload('x-pack/solutions/security/test/fixtures/es_archives/entity/risks'); await esArchiver.unload(path); - await esArchiver.unload( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + await entityStoreV2.teardown(); }); it('should be enriched with host risk score', async () => { diff --git a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/query/trial_license_complete_tier/custom_query.ts b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/query/trial_license_complete_tier/custom_query.ts index 5f76f28ee05cb..6fd322026bb49 100644 --- a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/query/trial_license_complete_tier/custom_query.ts +++ b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/query/trial_license_complete_tier/custom_query.ts @@ -77,12 +77,18 @@ import { import type { FtrProviderContext } from '../../../../../../ftr_provider_context'; import { EsArchivePathBuilder } from '../../../../../../es_archive_path_builder'; +import { EntityStoreV2EnrichmentSetup } from '../../entity_store_v2_enrichment_setup'; /** * Specific _id to use for some of the tests. If the archiver changes and you see errors * here, update this to a new value of a chosen auditbeat record and update the tests values. */ const ID = 'BhbXBmkBR346wHgn4PeZ'; +// Entity fields for the auditbeat record with _id = ID. +const ENRICHMENT_HOST_ID = '8cc95778cce5407c809480e8e32ad76b'; +const ENRICHMENT_HOST_NAME = 'suricata-zeek-sensor-toronto'; +const ENRICHMENT_HOST_EUID = `host:${ENRICHMENT_HOST_ID}`; +const ENRICHMENT_USER_NAME = 'root'; /** * Test coverage: @@ -97,6 +103,7 @@ export default ({ getService }: FtrProviderContext) => { const es = getService('es'); const log = getService('log'); const esDeleteAllIndices = getService('esDeleteAllIndices'); + const entityStoreV2 = EntityStoreV2EnrichmentSetup(getService); // TODO: add a new service for loading archiver files similar to "getService('es')" const config = getService('config'); const isServerless = config.get('serverless'); @@ -277,15 +284,43 @@ export default ({ getService }: FtrProviderContext) => { }); describe('with host and user risk indices', () => { - before(async () => { - await esArchiver.load('x-pack/solutions/security/test/fixtures/es_archives/entity/risks'); + before(async function () { + // Auditbeat host records carry host.id so the EUID is id-based (host:). + // Auditbeat user records carry host.id but no user.domain, so the EUID is local-namespace-based: + // user:@@local. entity.id must be supplied explicitly — the create API requires it. + if ( + !(await entityStoreV2.setup({ + hosts: [ + { + host: { name: ENRICHMENT_HOST_NAME, id: [ENRICHMENT_HOST_ID] }, + entity: { + id: ENRICHMENT_HOST_EUID, + type: 'host', + risk: { calculated_level: 'Critical', calculated_score_norm: 96 }, + }, + }, + ], + users: [ + { + user: { name: ENRICHMENT_USER_NAME }, + host: { id: [ENRICHMENT_HOST_ID] }, + entity: { + id: `user:${ENRICHMENT_USER_NAME}@${ENRICHMENT_HOST_ID}@local`, + type: 'user', + risk: { calculated_level: 'Low', calculated_score_norm: 11 }, + }, + }, + ], + })) + ) + return this.skip(); }); after(async () => { - await esArchiver.unload('x-pack/solutions/security/test/fixtures/es_archives/entity/risks'); + await entityStoreV2.teardown(); }); - it('@skipInServerlessMKI should have host and user risk score fields', async () => { + it('should have host and user risk score fields', async () => { const rule: QueryRuleCreateProps = { ...getRuleForAlertTesting(['auditbeat-*']), query: `_id:${ID}`, @@ -299,7 +334,7 @@ export default ({ getService }: FtrProviderContext) => { expect(previewAlerts[0]?._source?.user?.risk?.calculated_score_norm).toEqual(11); }); - it('@skipInServerlessMKI should have host and user risk score fields when suppression enabled on interval', async () => { + it('should have host and user risk score fields when suppression enabled on interval', async () => { const rule: QueryRuleCreateProps = { ...getRuleForAlertTesting(['auditbeat-*']), query: `_id:${ID}`, @@ -320,7 +355,7 @@ export default ({ getService }: FtrProviderContext) => { expect(previewAlerts[0]?._source?.user?.risk?.calculated_score_norm).toEqual(11); }); - it('@skipInServerlessMKI should have host and user risk score fields when suppression enabled on rule execution only', async () => { + it('should have host and user risk score fields when suppression enabled on rule execution only', async () => { const rule: QueryRuleCreateProps = { ...getRuleForAlertTesting(['auditbeat-*']), query: `_id:${ID}`, @@ -339,19 +374,37 @@ export default ({ getService }: FtrProviderContext) => { }); describe('with asset criticality', () => { - before(async () => { - await esArchiver.load( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + before(async function () { + if ( + !(await entityStoreV2.setup({ + hosts: [ + { + host: { name: ENRICHMENT_HOST_NAME, id: [ENRICHMENT_HOST_ID] }, + entity: { id: ENRICHMENT_HOST_EUID, type: 'host' }, + asset: { criticality: 'high_impact' }, + }, + ], + users: [ + { + user: { name: ENRICHMENT_USER_NAME }, + host: { id: [ENRICHMENT_HOST_ID] }, + entity: { + id: `user:${ENRICHMENT_USER_NAME}@${ENRICHMENT_HOST_ID}@local`, + type: 'user', + }, + asset: { criticality: 'extreme_impact' }, + }, + ], + })) + ) + return this.skip(); }); after(async () => { - await esArchiver.unload( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + await entityStoreV2.teardown(); }); - it('@skipInServerlessMKI should be enriched alert with criticality_level', async () => { + it('should be enriched alert with criticality_level', async () => { const rule: QueryRuleCreateProps = { ...getRuleForAlertTesting(['auditbeat-*']), query: `_id:${ID}`, @@ -362,7 +415,7 @@ export default ({ getService }: FtrProviderContext) => { expect(previewAlerts[0]?._source?.['user.asset.criticality']).toEqual('extreme_impact'); }); - it('@skipInServerlessMKI should be enriched alert with criticality_level when suppression enabled', async () => { + it('should be enriched alert with criticality_level when suppression enabled', async () => { const rule: QueryRuleCreateProps = { ...getRuleForAlertTesting(['auditbeat-*']), query: `_id:${ID}`, diff --git a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/threshold/trial_license_complete_tier/threshold.ts b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/threshold/trial_license_complete_tier/threshold.ts index d137e23b0f98b..0a220a3f30a3a 100644 --- a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/threshold/trial_license_complete_tier/threshold.ts +++ b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/threshold/trial_license_complete_tier/threshold.ts @@ -42,6 +42,11 @@ import { } from '../../../../utils'; import type { FtrProviderContext } from '../../../../../../ftr_provider_context'; import { EsArchivePathBuilder } from '../../../../../../es_archive_path_builder'; +import { EntityStoreV2EnrichmentSetup } from '../../entity_store_v2_enrichment_setup'; + +// Sorted by host.name: suricata-sensor-london < suricata-zeek-sensor-toronto. +const LONDON_HOST_NAME = 'suricata-sensor-london'; +const TORONTO_HOST_NAME = 'suricata-zeek-sensor-toronto'; export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); @@ -53,6 +58,7 @@ export default ({ getService }: FtrProviderContext) => { const isServerless = config.get('serverless'); const dataPathBuilder = new EsArchivePathBuilder(isServerless); const path = dataPathBuilder.getPath('auditbeat/hosts'); + const entityStoreV2 = EntityStoreV2EnrichmentSetup(getService); describe('@ess @serverless @serverlessQA Threshold type rules', () => { before(async () => { @@ -427,15 +433,40 @@ export default ({ getService }: FtrProviderContext) => { }); describe('with host risk index', () => { - before(async () => { - await esArchiver.load('x-pack/solutions/security/test/fixtures/es_archives/entity/risks'); + before(async function () { + // Threshold alerts aggregate by host.name and may not carry host.id in their source, + // so the EUID is name-based (host:). Omit host.id to ensure the entity EUID + // matches what the detection engine computes from the threshold alert. + if ( + !(await entityStoreV2.setup({ + hosts: [ + { + host: { name: LONDON_HOST_NAME }, + entity: { + id: `host:${LONDON_HOST_NAME}`, + type: 'host', + risk: { calculated_level: 'Low', calculated_score_norm: 20 }, + }, + }, + { + host: { name: TORONTO_HOST_NAME }, + entity: { + id: `host:${TORONTO_HOST_NAME}`, + type: 'host', + risk: { calculated_level: 'Critical', calculated_score_norm: 96 }, + }, + }, + ], + })) + ) + return this.skip(); }); after(async () => { - await esArchiver.unload('x-pack/solutions/security/test/fixtures/es_archives/entity/risks'); + await entityStoreV2.teardown(); }); - it('@skipInServerlessMKI should be enriched with host risk score', async () => { + it('should be enriched with host risk score', async () => { const rule: ThresholdRuleCreateProps = { ...getThresholdRuleForAlertTesting(['auditbeat-*']), threshold: { @@ -454,19 +485,28 @@ export default ({ getService }: FtrProviderContext) => { }); describe('with asset criticality', () => { - before(async () => { - await esArchiver.load( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + before(async function () { + // Only the first alert (sorted by host.name) is asserted on — suricata-sensor-london. + // Use name-based EUID for the same reason as the risk index describe above. + if ( + !(await entityStoreV2.setup({ + hosts: [ + { + host: { name: LONDON_HOST_NAME }, + entity: { id: `host:${LONDON_HOST_NAME}`, type: 'host' }, + asset: { criticality: 'high_impact' }, + }, + ], + })) + ) + return this.skip(); }); after(async () => { - await esArchiver.unload( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + await entityStoreV2.teardown(); }); - it('@skipInServerlessMKI should be enriched alert with criticality_level', async () => { + it('should be enriched alert with criticality_level', async () => { const rule: ThresholdRuleCreateProps = { ...getThresholdRuleForAlertTesting(['auditbeat-*']), threshold: { diff --git a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/threshold/trial_license_complete_tier/threshold_alert_suppression.ts b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/threshold/trial_license_complete_tier/threshold_alert_suppression.ts index e43cc58727f56..5254b73a6dc14 100644 --- a/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/threshold/trial_license_complete_tier/threshold_alert_suppression.ts +++ b/x-pack/solutions/security/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/threshold/trial_license_complete_tier/threshold_alert_suppression.ts @@ -37,6 +37,11 @@ import { } from '../../../../utils'; import type { FtrProviderContext } from '../../../../../../ftr_provider_context'; import { EsArchivePathBuilder } from '../../../../../../es_archive_path_builder'; +import { EntityStoreV2EnrichmentSetup } from '../../entity_store_v2_enrichment_setup'; + +// Sorted by host.name: suricata-sensor-london < suricata-zeek-sensor-toronto. +const LONDON_HOST_NAME = 'suricata-sensor-london'; +const TORONTO_HOST_NAME = 'suricata-zeek-sensor-toronto'; export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); @@ -48,6 +53,7 @@ export default ({ getService }: FtrProviderContext) => { const isServerless = config.get('serverless'); const dataPathBuilder = new EsArchivePathBuilder(isServerless); const path = dataPathBuilder.getPath('auditbeat/hosts'); + const entityStoreV2 = EntityStoreV2EnrichmentSetup(getService); // NOTE: Add to second quality gate after feature is GA describe('@ess @serverless Threshold type rules, alert suppression', () => { @@ -960,13 +966,36 @@ export default ({ getService }: FtrProviderContext) => { ); }); - describe('@skipInServerlessMKI with host risk index', () => { - before(async () => { - await esArchiver.load('x-pack/solutions/security/test/fixtures/es_archives/entity/risks'); + describe('with host risk index', () => { + before(async function () { + // Threshold alerts aggregate by host.name and may not carry host.id, so use name-based EUIDs. + if ( + !(await entityStoreV2.setup({ + hosts: [ + { + host: { name: LONDON_HOST_NAME }, + entity: { + id: `host:${LONDON_HOST_NAME}`, + type: 'host', + risk: { calculated_level: 'Low', calculated_score_norm: 20 }, + }, + }, + { + host: { name: TORONTO_HOST_NAME }, + entity: { + id: `host:${TORONTO_HOST_NAME}`, + type: 'host', + risk: { calculated_level: 'Critical', calculated_score_norm: 96 }, + }, + }, + ], + })) + ) + return this.skip(); }); after(async () => { - await esArchiver.unload('x-pack/solutions/security/test/fixtures/es_archives/entity/risks'); + await entityStoreV2.teardown(); }); it('should be enriched with host risk score', async () => { @@ -993,17 +1022,25 @@ export default ({ getService }: FtrProviderContext) => { }); }); - describe('@skipInServerlessMKI with asset criticality', () => { - before(async () => { - await esArchiver.load( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + describe('with asset criticality', () => { + before(async function () { + // Use name-based EUID for threshold alerts (same reason as risk index describe above). + if ( + !(await entityStoreV2.setup({ + hosts: [ + { + host: { name: LONDON_HOST_NAME }, + entity: { id: `host:${LONDON_HOST_NAME}`, type: 'host' }, + asset: { criticality: 'high_impact' }, + }, + ], + })) + ) + return this.skip(); }); after(async () => { - await esArchiver.unload( - 'x-pack/solutions/security/test/fixtures/es_archives/asset_criticality' - ); + await entityStoreV2.teardown(); }); it('should be enriched alert with criticality_level', async () => {