From 2c3464ca8457e09f51852c6c0d60ab7f6e21afc0 Mon Sep 17 00:00:00 2001 From: Maxim Palenov Date: Thu, 31 Aug 2023 12:28:16 +0200 Subject: [PATCH] [Security Solution] Unskip related integrations tests (#164933) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Fixes: https://github.com/elastic/kibana/issues/154663** **Fixes: https://github.com/elastic/kibana/issues/153684** ## Summary This PR unskips Cypress rule related integration tests ([related_integrations.cy.ts](https://github.com/elastic/kibana/blob/main/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/related_integrations/related_integrations.cy.ts)). ## Details Testing approach has changed. Instead of importing a rule and installing agent and package policies via Fleet UI the rule is created by mocking a prebuilt rule asset and Fleet API is used to install required integrations. Along the way it required to add and update some testing selectors as UI had changed while the tests were skipped. ## Flaky test runner [related_integrations.cy.ts (150 runs)](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2995) 🟢 --- .../components/event_details/helpers.tsx | 1 + .../pages/rule_details/index.tsx | 2 +- .../rule_creation/custom_query_rule.cy.ts | 3 +- .../custom_query_rule_data_view.cy.ts | 3 +- .../event_correlation_rule.cy.ts | 3 +- .../rule_creation/indicator_match_rule.cy.ts | 7 +- .../rule_creation/new_terms_rule.cy.ts | 3 +- .../rule_creation/override.cy.ts | 5 +- .../rule_creation/threshold_rule.cy.ts | 3 +- .../related_integrations.cy.ts | 482 ++++++++++++------ .../endpoint_exceptions.cy.ts | 7 +- .../alerts/building_block_alerts.cy.ts | 6 +- .../fixtures/related_integrations.ndjson | 2 - .../cypress/objects/event.ts | 17 + .../cypress/objects/types.ts | 13 + .../cypress/screens/common/page.ts | 4 +- .../cypress/screens/create_new_rule.ts | 2 - .../cypress/screens/rule_details.ts | 7 +- .../cypress/screens/rule_details_flyout.ts | 11 + .../cypress/tasks/api_calls/elasticsearch.ts | 26 +- .../cypress/tasks/api_calls/prebuilt_rules.ts | 40 +- .../cypress/tasks/common.ts | 13 +- .../cypress/tasks/create_new_rule.ts | 12 - .../cypress/tasks/integrations.ts | 86 +++- .../cypress/tasks/rule_details.ts | 20 +- .../cypress/tasks/rule_details_flyout.ts | 16 + .../cypress/tsconfig.json | 1 + 27 files changed, 549 insertions(+), 246 deletions(-) delete mode 100644 x-pack/test/security_solution_cypress/cypress/fixtures/related_integrations.ndjson create mode 100644 x-pack/test/security_solution_cypress/cypress/objects/event.ts create mode 100644 x-pack/test/security_solution_cypress/cypress/screens/rule_details_flyout.ts create mode 100644 x-pack/test/security_solution_cypress/cypress/tasks/rule_details_flyout.ts diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/helpers.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/helpers.tsx index 711ffc34e49d7..5133faaeb9f4f 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/helpers.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/helpers.tsx @@ -32,6 +32,7 @@ export const search = { incremental: true, placeholder: i18n.PLACEHOLDER, schema: true, + 'data-test-subj': 'search-input', }, }; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx index 5f9e1f17dba18..44e41f0f9ebd3 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx @@ -410,7 +410,7 @@ const RuleDetailsPageComponent: React.FC = ({ ) : ( { login(); + deleteAlertsAndRules(); }); it('Creates and enables a new custom rule with override option', function () { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/threshold_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/threshold_rule.cy.ts index c3d3deb58d520..078be723c39ac 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/threshold_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/threshold_rule.cy.ts @@ -43,7 +43,7 @@ import { TIMELINE_TEMPLATE_DETAILS, } from '../../../screens/rule_details'; -import { getDetails } from '../../../tasks/rule_details'; +import { getDetails, waitForTheRuleToBeExecuted } from '../../../tasks/rule_details'; import { expectNumberOfRules, goToRuleDetails } from '../../../tasks/alerts_detection_rules'; import { cleanKibana, deleteAlertsAndRules } from '../../../tasks/common'; import { @@ -53,7 +53,6 @@ import { fillScheduleRuleAndContinue, selectThresholdRuleType, waitForAlertsToPopulate, - waitForTheRuleToBeExecuted, } from '../../../tasks/create_new_rule'; import { login, visitWithoutDateRange } from '../../../tasks/login'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/related_integrations/related_integrations.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/related_integrations/related_integrations.cy.ts index 0d41e51852203..2d543bcfd35fc 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/related_integrations/related_integrations.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/related_integrations/related_integrations.cy.ts @@ -5,239 +5,405 @@ * 2.0. */ -import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../../urls/navigation'; - +import { omit } from 'lodash'; +import { PerformRuleInstallationResponseBody } from '@kbn/security-solution-plugin/common/api/detection_engine'; +import { filterBy, openTable } from '../../../../tasks/rule_details_flyout'; +import { generateEvent } from '../../../../objects/event'; +import { createDocument, deleteDataStream } from '../../../../tasks/api_calls/elasticsearch'; +import { createRuleAssetSavedObject } from '../../../../helpers/rules'; import { FIELD } from '../../../../screens/alerts_details'; -import { INTEGRATIONS, INTEGRATIONS_STATUS } from '../../../../screens/rule_details'; +import { INTEGRATION_LINK, INTEGRATION_STATUS } from '../../../../screens/rule_details'; import { INTEGRATIONS_POPOVER, INTEGRATIONS_POPOVER_TITLE, RULE_NAME, } from '../../../../screens/alerts_detection_rules'; - +import { + installPrebuiltRuleAssets, + installAllPrebuiltRulesRequest, + SAMPLE_PREBUILT_RULE, +} from '../../../../tasks/api_calls/prebuilt_rules'; import { cleanFleet } from '../../../../tasks/api_calls/fleet'; -import { importRule } from '../../../../tasks/api_calls/rules'; import { disableRelatedIntegrations, enableRelatedIntegrations, } from '../../../../tasks/api_calls/kibana_advanced_settings'; - -import { cleanKibana } from '../../../../tasks/common'; -import { login, visit } from '../../../../tasks/login'; +import { deleteAlertsAndRules } from '../../../../tasks/common'; +import { + login, + visitSecurityDetectionRulesPage, + visitWithoutDateRange, +} from '../../../../tasks/login'; import { expandFirstAlert } from '../../../../tasks/alerts'; -import { filterBy, openTable } from '../../../../tasks/alerts_details'; import { waitForAlertsToPopulate } from '../../../../tasks/create_new_rule'; -import { installAwsCloudFrontWithPolicy } from '../../../../tasks/integrations'; import { - enableRule, - goToTheRuleDetailsOf, + installIntegrations, + PackagePolicyWithoutAgentPolicyId, +} from '../../../../tasks/integrations'; +import { + disableAutoRefresh, openIntegrationsPopover, - waitForRulesTableToShow, - waitForRuleToUpdate, } from '../../../../tasks/alerts_detection_rules'; - -/* -Note that the rule we are using for testing purposes has the following characteristics, changing that may affect the coverage. - -- Single-integration - - Package: system -- Multi-integration package - - Package: aws - - Integration: cloudtrail - - Integration: cloudfront -- Not existing package: - - Package: unknown -- Not existing integration & existing package: - - Package: aws - - Integration: unknown -*/ +import { ruleDetailsUrl } from '../../../../urls/navigation'; +import { enablesRule, waitForPageToBeLoaded } from '../../../../tasks/rule_details'; describe('Related integrations', { tags: ['@ess', '@brokenInServerless'] }, () => { - before(() => { - cleanKibana(); - login(); - importRule('related_integrations.ndjson'); + const DATA_STREAM_NAME = 'logs-related-integrations-test'; + const PREBUILT_RULE_NAME = 'Prebuilt rule with related integrations'; + const RULE_RELATED_INTEGRATIONS: IntegrationDefinition[] = [ + { + package: 'aws', + version: '1.17.0', + integration: 'cloudfront', + installed: true, + enabled: true, + }, + { + package: 'aws', + version: '1.17.0', + integration: 'cloudtrail', + installed: true, + enabled: false, + }, + { package: 'aws', version: '1.17.0', integration: 'unknown', installed: false, enabled: false }, + { package: 'system', version: '1.17.0', installed: true, enabled: true }, + ]; + const PREBUILT_RULE = createRuleAssetSavedObject({ + name: PREBUILT_RULE_NAME, + index: [DATA_STREAM_NAME], + query: '*:*', + rule_id: 'rule_1', + related_integrations: RULE_RELATED_INTEGRATIONS.map((x) => omit(x, ['installed', 'enabled'])), }); - context('integrations not installed', () => { - const rule = { - name: 'Related integrations rule', - integrations: ['Aws Cloudfront', 'Aws Cloudtrail', 'Aws Unknown', 'System'], - enabledIntegrations: '0', - }; - - before(() => { - cleanFleet(); - }); - - beforeEach(() => { - login(); - visit(DETECTIONS_RULE_MANAGEMENT_URL); - waitForRulesTableToShow(); - }); + beforeEach(() => { + login(); + cleanFleet(); + deleteAlertsAndRules(); + addAndInstallPrebuiltRules([PREBUILT_RULE]); + }); - it('should display a badge with the installed integrations on the rule management page', () => { - cy.get(INTEGRATIONS_POPOVER).should( - 'have.text', - `${rule.enabledIntegrations}/${rule.integrations.length} integrations` - ); - }); + describe('integrations not installed', () => { + describe('rules management table', () => { + beforeEach(() => { + visitSecurityDetectionRulesPage(); + disableAutoRefresh(); + }); - it('should display a popover when clicking the badge with the installed integrations on the rule management page', () => { - openIntegrationsPopover(); + it('should display a badge with the installed integrations', () => { + cy.get(INTEGRATIONS_POPOVER).should( + 'have.text', + `0/${RULE_RELATED_INTEGRATIONS.length} integrations` + ); + }); - cy.get(INTEGRATIONS_POPOVER_TITLE).should( - 'have.text', - `[${rule.integrations.length}] Related integrations available` - ); - cy.get(INTEGRATIONS).should('have.length', rule.integrations.length); - cy.get(INTEGRATIONS_STATUS).should('have.length', rule.integrations.length); + it('should display a popover when clicking the badge with the installed integrations', () => { + openIntegrationsPopover(); - rule.integrations.forEach((integration, index) => { - cy.get(INTEGRATIONS).eq(index).should('contain', integration); - cy.get(INTEGRATIONS_STATUS).eq(index).should('have.text', 'Not installed'); + cy.get(INTEGRATIONS_POPOVER_TITLE).should( + 'have.text', + `[${RULE_RELATED_INTEGRATIONS.length}] Related integrations available` + ); + cy.get(INTEGRATION_LINK).should('have.length', RULE_RELATED_INTEGRATIONS.length); + cy.get(INTEGRATION_STATUS).should('have.length', RULE_RELATED_INTEGRATIONS.length); + + RULE_RELATED_INTEGRATIONS.forEach((integration, index) => { + cy.get(INTEGRATION_LINK).eq(index).contains(getIntegrationName(integration), { + matchCase: false, + }); + cy.get(INTEGRATION_STATUS).eq(index).should('have.text', 'Not installed'); + }); }); }); - it('should display the integrations on the definition section', () => { - goToTheRuleDetailsOf(rule.name); + describe('rule details', () => { + beforeEach(() => { + visitFirstInstalledPrebuiltRuleDetailsPage(); + }); - cy.get(INTEGRATIONS).should('have.length', rule.integrations.length); - cy.get(INTEGRATIONS_STATUS).should('have.length', rule.integrations.length); + it('should display the integrations in the definition section', () => { + cy.get(INTEGRATION_LINK).should('have.length', RULE_RELATED_INTEGRATIONS.length); + cy.get(INTEGRATION_STATUS).should('have.length', RULE_RELATED_INTEGRATIONS.length); - rule.integrations.forEach((integration, index) => { - cy.get(INTEGRATIONS).eq(index).should('contain', integration); - cy.get(INTEGRATIONS_STATUS).eq(index).should('have.text', 'Not installed'); + RULE_RELATED_INTEGRATIONS.forEach((integration, index) => { + cy.get(INTEGRATION_LINK).eq(index).contains(getIntegrationName(integration), { + matchCase: false, + }); + cy.get(INTEGRATION_STATUS).eq(index).should('have.text', 'Not installed'); + }); }); }); }); - context.skip( - 'installed integrations: Amazon CloudFront, AWS CloudTrail, System, enabled integrations: Amazon CloudFront, Aws Cloudfront, System', - () => { - const rule = { - name: 'Related integrations rule', - integrations: [ - { name: 'AWS Cloudfront', installed: true, enabled: true }, - { name: 'AWS CloudTrail', installed: true, enabled: false }, - { name: 'Aws Unknown', installed: false, enabled: false }, - { name: 'System', installed: true, enabled: true }, + describe('integrations installed (AWS CloudFront (enabled), AWS CloudTrail (disabled), System (enabled))', () => { + beforeEach(() => { + installIntegrations({ + packages: [ + { name: 'aws', version: '1.17.0' }, + { name: 'system', version: '1.17.0' }, ], - enabledIntegrations: '2', - }; - - before(() => { - cleanFleet().then(() => { - installAwsCloudFrontWithPolicy(); - }); + agentPolicy: { + name: 'Agent policy', + namespace: 'default', + monitoring_enabled: ['logs'], + inactivity_timeout: 1209600, + }, + packagePolicy: AWS_PACKAGE_POLICY, }); + }); + describe('rules management table', () => { beforeEach(() => { - login(); - visit(DETECTIONS_RULE_MANAGEMENT_URL); - waitForRulesTableToShow(); + visitSecurityDetectionRulesPage(); + disableAutoRefresh(); }); - it('should display a badge with the installed integrations on the rule management page', () => { + it('should display a badge with the installed integrations', () => { + const enabledIntegrations = RULE_RELATED_INTEGRATIONS.filter((x) => x.enabled).length; + const totalIntegrations = RULE_RELATED_INTEGRATIONS.length; + cy.get(INTEGRATIONS_POPOVER).should( 'have.text', - `${rule.enabledIntegrations}/${rule.integrations.length} integrations` + `${enabledIntegrations}/${totalIntegrations} integrations` ); }); - it('should display a popover when clicking the badge with the installed integrations on the rule management page', () => { + it('should display a popover when clicking the badge with the installed integrations', () => { openIntegrationsPopover(); cy.get(INTEGRATIONS_POPOVER_TITLE).should( 'have.text', - `[${rule.integrations.length}] Related integrations available` + `[${RULE_RELATED_INTEGRATIONS.length}] Related integrations available` ); - cy.get(INTEGRATIONS).should('have.length', rule.integrations.length); - cy.get(INTEGRATIONS_STATUS).should('have.length', rule.integrations.length); - - rule.integrations.forEach((integration, index) => { - let expectedStatus = integration.installed ? 'Installed' : 'Not installed'; - if (integration.enabled) expectedStatus += ': enabled'; - - cy.get(INTEGRATIONS).eq(index).should('contain', integration.name); - cy.get(INTEGRATIONS_STATUS).eq(index).should('have.text', expectedStatus); + cy.get(INTEGRATION_LINK).should('have.length', RULE_RELATED_INTEGRATIONS.length); + cy.get(INTEGRATION_STATUS).should('have.length', RULE_RELATED_INTEGRATIONS.length); + + RULE_RELATED_INTEGRATIONS.forEach((integration, index) => { + cy.get(INTEGRATION_LINK).eq(index).contains(getIntegrationName(integration), { + matchCase: false, + }); + cy.get(INTEGRATION_STATUS) + .eq(index) + .should('have.text', getIntegrationStatus(integration)); }); }); + }); - it('should display the integrations on the definition section', () => { - goToTheRuleDetailsOf(rule.name); - - cy.get(INTEGRATIONS).should('have.length', rule.integrations.length); - cy.get(INTEGRATIONS_STATUS).should('have.length', rule.integrations.length); - - rule.integrations.forEach((integration, index) => { - let expectedStatus = integration.installed ? 'Installed' : 'Not installed'; - if (integration.enabled) expectedStatus += ': enabled'; + describe('rule details', () => { + beforeEach(() => { + visitFirstInstalledPrebuiltRuleDetailsPage(); + }); - cy.get(INTEGRATIONS).eq(index).should('contain', integration.name); - cy.get(INTEGRATIONS_STATUS).eq(index).should('have.text', expectedStatus); + it('should display the integrations in the definition section', () => { + cy.get(INTEGRATION_LINK).should('have.length', RULE_RELATED_INTEGRATIONS.length); + cy.get(INTEGRATION_STATUS).should('have.length', RULE_RELATED_INTEGRATIONS.length); + + RULE_RELATED_INTEGRATIONS.forEach((integration, index) => { + cy.get(INTEGRATION_LINK).eq(index).contains(getIntegrationName(integration), { + matchCase: false, + }); + cy.get(INTEGRATION_STATUS) + .eq(index) + .should('have.text', getIntegrationStatus(integration)); }); }); it('the alerts generated should have a "kibana.alert.rule.parameters.related_integrations" field containing the integrations', () => { - const firstRule = 0; - const relatedIntegrationsField = 'kibana.alert.rule.parameters.related_integrations'; - const expectedRelatedIntegrationsText = - '{"package":"system","version":"1.17.0"}{"package":"aws","integration":"cloudtrail","version":"1.17.0"}{"package":"aws","integration":"cloudfront","version":"1.17.0"}{"package":"aws","integration":"unknown","version":"1.17.0"}'; - - enableRule(firstRule); - waitForRuleToUpdate(); - goToTheRuleDetailsOf(rule.name); + const RELATED_INTEGRATION_FIELD = 'kibana.alert.rule.parameters.related_integrations'; + + deleteDataStream(DATA_STREAM_NAME); + createDocument(DATA_STREAM_NAME, generateEvent()); + + waitForPageToBeLoaded(PREBUILT_RULE_NAME); + enablesRule(); waitForAlertsToPopulate(); expandFirstAlert(); openTable(); - filterBy(relatedIntegrationsField); - cy.get(FIELD(relatedIntegrationsField)).should( - 'have.text', - expectedRelatedIntegrationsText - ); + filterBy(RELATED_INTEGRATION_FIELD); + + RULE_RELATED_INTEGRATIONS.forEach((integration) => { + cy.contains( + FIELD(RELATED_INTEGRATION_FIELD), + `{"package":"${integration.package}"${ + integration.integration ? `,"integration":"${integration.integration}"` : '' + },"version":"${integration.version}"}` + ); + }); }); - } - ); - - context('related Integrations Advanced Setting is disabled', () => { - const rule = { - name: 'Related integrations rule', - integrations: ['Aws Cloudfront', 'Aws Cloudtrail', 'Aws Unknown', 'System'], - enabledIntegrations: '0', - }; + }); + }); + describe('related Integrations Advanced Setting is disabled', () => { before(() => { - cleanFleet().then(() => { - disableRelatedIntegrations(); - }); + disableRelatedIntegrations(); }); after(() => { enableRelatedIntegrations(); }); - beforeEach(() => { - login(); - visit(DETECTIONS_RULE_MANAGEMENT_URL); - waitForRulesTableToShow(); - }); + describe('rules management table', () => { + beforeEach(() => { + visitSecurityDetectionRulesPage(); + disableAutoRefresh(); + }); - it('should not display a badge with the installed integrations on the rule management page', () => { - cy.get(RULE_NAME).should('have.text', rule.name); - cy.get(INTEGRATIONS).should('not.exist'); + it('should not display a badge with the installed integrations', () => { + cy.get(RULE_NAME).should('have.text', PREBUILT_RULE_NAME); + cy.get(INTEGRATION_LINK).should('not.exist'); + }); }); - it('should display the integrations on the definition section', () => { - goToTheRuleDetailsOf(rule.name); + describe('rule details', () => { + beforeEach(() => { + visitFirstInstalledPrebuiltRuleDetailsPage(); + }); - cy.get(INTEGRATIONS).should('have.length', rule.integrations.length); - cy.get(INTEGRATIONS_STATUS).should('have.length', rule.integrations.length); + it('should display the integrations in the definition section', () => { + cy.get(INTEGRATION_LINK).should('have.length', RULE_RELATED_INTEGRATIONS.length); + cy.get(INTEGRATION_STATUS).should('have.length', RULE_RELATED_INTEGRATIONS.length); - rule.integrations.forEach((integration, index) => { - cy.get(INTEGRATIONS).eq(index).should('contain', integration); - cy.get(INTEGRATIONS_STATUS).eq(index).should('have.text', 'Not installed'); + RULE_RELATED_INTEGRATIONS.forEach((integration, index) => { + cy.get(INTEGRATION_LINK).eq(index).contains(getIntegrationName(integration), { + matchCase: false, + }); + cy.get(INTEGRATION_STATUS).eq(index).should('have.text', 'Not installed'); + }); }); }); }); }); + +const INSTALLED_PREBUILT_RULES_RESPONSE_ALIAS = 'prebuiltRules'; + +function addAndInstallPrebuiltRules(rules: Array): void { + installPrebuiltRuleAssets(rules); + installAllPrebuiltRulesRequest().as(INSTALLED_PREBUILT_RULES_RESPONSE_ALIAS); +} + +function visitFirstInstalledPrebuiltRuleDetailsPage(): void { + cy.get>( + `@${INSTALLED_PREBUILT_RULES_RESPONSE_ALIAS}` + ).then((response) => visitWithoutDateRange(ruleDetailsUrl(response.body.results.created[0].id))); +} + +interface IntegrationDefinition { + package: string; + version: string; + installed: boolean; + enabled: boolean; + integration?: string; +} + +function getIntegrationName(integration: IntegrationDefinition): string { + return `${integration.package} ${integration.integration ?? ''}`.trim(); +} + +function getIntegrationStatus(integration: IntegrationDefinition): string { + return `${integration.installed ? 'Installed' : 'Not installed'}${ + integration.enabled ? ': enabled' : '' + }`.trim(); +} + +/** + * AWS package policy has been generated by Kibana. Instead of copying the whole output the policy below + * contains only required for testing inputs. + */ +const AWS_PACKAGE_POLICY: PackagePolicyWithoutAgentPolicyId = { + package: { + name: 'aws', + version: '1.17.0', + }, + name: 'aws-1', + namespace: 'default', + inputs: { + 'cloudtrail-aws-s3': { + enabled: false, + streams: { + 'aws.cloudtrail': { + enabled: true, + vars: { + fips_enabled: false, + tags: ['forwarded', 'aws-cloudtrail'], + preserve_original_event: false, + cloudtrail_regex: '/CloudTrail/', + cloudtrail_digest_regex: '/CloudTrail-Digest/', + cloudtrail_insight_regex: '/CloudTrail-Insight/', + max_number_of_messages: 5, + }, + }, + }, + }, + 'elb-aws-s3': { + enabled: false, + streams: { + 'aws.elb_logs': { + enabled: true, + vars: { + fips_enabled: false, + tags: ['forwarded', 'aws-elb-logs'], + preserve_original_event: false, + max_number_of_messages: 5, + }, + }, + }, + }, + 'firewall-aws-s3': { + enabled: false, + streams: { + 'aws.firewall_logs': { + enabled: true, + vars: { + fips_enabled: false, + tags: ['forwarded', 'aws-firewall-logs'], + preserve_original_event: false, + max_number_of_messages: 5, + }, + }, + }, + }, + 's3-aws-s3': { + enabled: false, + streams: { + 'aws.s3access': { + enabled: true, + vars: { + fips_enabled: false, + tags: ['forwarded', 'aws-s3access'], + preserve_original_event: false, + max_number_of_messages: 5, + }, + }, + }, + }, + 'waf-aws-s3': { + enabled: false, + streams: { + 'aws.waf': { + enabled: true, + vars: { + fips_enabled: false, + tags: ['forwarded', 'aws-waf'], + preserve_original_event: false, + max_number_of_messages: 5, + }, + }, + }, + }, + 'cloudfront-aws-s3': { + enabled: true, + streams: { + 'aws.cloudfront_logs': { + enabled: true, + vars: { + queue_url: 'https://example.com', + fips_enabled: false, + tags: ['forwarded', 'aws-cloudfront'], + preserve_original_event: false, + max_number_of_messages: 5, + }, + }, + }, + }, + }, +}; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/endpoint_exceptions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/endpoint_exceptions.cy.ts index 920ddd62909b3..e9c07294e62d3 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/endpoint_exceptions.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/endpoint_exceptions.cy.ts @@ -17,10 +17,7 @@ import { login, visitWithoutDateRange } from '../../../tasks/login'; import { getEndpointRule } from '../../../objects/rule'; import { goToRuleDetails } from '../../../tasks/alerts_detection_rules'; import { createRule } from '../../../tasks/api_calls/rules'; -import { - waitForAlertsToPopulate, - waitForTheRuleToBeExecuted, -} from '../../../tasks/create_new_rule'; +import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule'; import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation'; import { addExceptionEntryFieldValueAndSelectSuggestion, @@ -38,7 +35,7 @@ import { EXCEPTION_CARD_ITEM_NAME, EXCEPTION_ITEM_VIEWER_CONTAINER, } from '../../../screens/exceptions'; -import { goToEndpointExceptionsTab } from '../../../tasks/rule_details'; +import { goToEndpointExceptionsTab, waitForTheRuleToBeExecuted } from '../../../tasks/rule_details'; // See https://github.com/elastic/kibana/issues/163967 describe.skip( diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/building_block_alerts.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/building_block_alerts.cy.ts index 0ed530adad451..d57ab19a0f4fe 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/building_block_alerts.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/building_block_alerts.cy.ts @@ -12,11 +12,9 @@ import { OVERVIEW } from '../../../screens/security_header'; import { goToRuleDetails } from '../../../tasks/alerts_detection_rules'; import { createRule } from '../../../tasks/api_calls/rules'; import { cleanKibana } from '../../../tasks/common'; -import { - waitForAlertsToPopulate, - waitForTheRuleToBeExecuted, -} from '../../../tasks/create_new_rule'; +import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule'; import { login, visitWithoutDateRange } from '../../../tasks/login'; +import { waitForTheRuleToBeExecuted } from '../../../tasks/rule_details'; import { navigateFromHeaderTo } from '../../../tasks/security_header'; import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation'; diff --git a/x-pack/test/security_solution_cypress/cypress/fixtures/related_integrations.ndjson b/x-pack/test/security_solution_cypress/cypress/fixtures/related_integrations.ndjson deleted file mode 100644 index f121d07f4610f..0000000000000 --- a/x-pack/test/security_solution_cypress/cypress/fixtures/related_integrations.ndjson +++ /dev/null @@ -1,2 +0,0 @@ -{"id":"6cc39c80-da3a-11ec-9fce-65c1a0bee904","updated_at":"2022-05-23T01:48:23.422Z","updated_by":"elastic","created_at":"2022-05-23T01:48:20.940Z","created_by":"elastic","name":"Related integrations rule","tags":["Elastic","Endpoint Security"],"interval":"5m","enabled":false,"description":"Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.","risk_score":47,"severity":"medium","license":"Elastic License v2","output_index":".siem-signals-default","meta":{"from":"5m"},"rule_name_override":"message","timestamp_override":"event.ingested","author":["Elastic"],"false_positives":[],"from":"now-50000h","rule_id":"2c66bf23-6ae9-4eb2-859e-446bea181ae9","max_signals":10000,"risk_score_mapping":[{"field":"event.risk_score","operator":"equals","value":""}],"severity_mapping":[{"field":"event.severity","operator":"equals","severity":"low","value":"21"},{"field":"event.severity","operator":"equals","severity":"medium","value":"47"},{"field":"event.severity","operator":"equals","severity":"high","value":"73"},{"field":"event.severity","operator":"equals","severity":"critical","value":"99"}],"threat":[],"to":"now","references":[],"version":7,"exceptions_list":[{"id":"endpoint_list","list_id":"endpoint_list","namespace_type":"agnostic","type":"endpoint"}],"immutable":false,"related_integrations":[{"package":"system","version":"1.17.0"},{"package":"aws","integration":"cloudtrail","version":"1.17.0"},{"package":"aws","integration":"cloudfront","version":"1.17.0"},{"package":"aws","integration":"unknown","version":"1.17.0"}],"type":"query","language":"kuery","index":["auditbeat-*"],"query":"*:*","filters":[],"throttle":"no_actions","actions":[]} -{"exported_count":1,"exported_rules_count":1,"missing_rules":[],"missing_rules_count":0,"exported_exception_list_count":0,"exported_exception_list_item_count":0,"missing_exception_list_item_count":0,"missing_exception_list_items":[],"missing_exception_lists":[],"missing_exception_lists_count":0} diff --git a/x-pack/test/security_solution_cypress/cypress/objects/event.ts b/x-pack/test/security_solution_cypress/cypress/objects/event.ts new file mode 100644 index 0000000000000..ea7a61f222eec --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/objects/event.ts @@ -0,0 +1,17 @@ +/* + * 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 { SecurityEvent } from './types'; + +export function generateEvent(extra: Record = {}): SecurityEvent { + return { + '@timestamp': Date.now(), + ecs: { version: '1.4.0' }, + event: { kind: 'event', category: 'process', type: 'start' }, + ...extra, + }; +} diff --git a/x-pack/test/security_solution_cypress/cypress/objects/types.ts b/x-pack/test/security_solution_cypress/cypress/objects/types.ts index b61580dd0287d..7050a97de91c4 100644 --- a/x-pack/test/security_solution_cypress/cypress/objects/types.ts +++ b/x-pack/test/security_solution_cypress/cypress/objects/types.ts @@ -12,3 +12,16 @@ export type CreateRulePropsRewrites = Partial { rootRequest({ method: 'DELETE', - url: `${Cypress.env('ELASTICSEARCH_URL')}/${index}`, + url: `${Cypress.env('ELASTICSEARCH_URL')}/${index}?refresh=wait_for`, headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' }, failOnStatusCode: false, }); }; +export const deleteDataStream = (dataStreamName: string) => { + rootRequest({ + method: 'DELETE', + url: `${Cypress.env('ELASTICSEARCH_URL')}/_data_stream/${dataStreamName}`, + headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' }, + failOnStatusCode: false, + }); +}; + +export const deleteAllDocuments = (target: string) => + rootRequest({ + method: 'POST', + url: `${Cypress.env( + 'ELASTICSEARCH_URL' + )}/${target}/_delete_by_query?conflicts=proceed&scroll_size=10000&refresh`, + body: { + query: { + match_all: {}, + }, + }, + }); + export const createIndex = (indexName: string, properties: Record) => rootRequest({ method: 'PUT', @@ -29,7 +51,7 @@ export const createIndex = (indexName: string, properties: Record) => rootRequest({ method: 'POST', - url: `${Cypress.env('ELASTICSEARCH_URL')}/${indexName}/_doc`, + url: `${Cypress.env('ELASTICSEARCH_URL')}/${indexName}/_doc?refresh=wait_for`, body: document, }); diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/prebuilt_rules.ts b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/prebuilt_rules.ts index 756a5f25d540c..48b21115b9895 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/prebuilt_rules.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/prebuilt_rules.ts @@ -5,6 +5,10 @@ * 2.0. */ +import { + PerformRuleInstallationResponseBody, + PERFORM_RULE_INSTALLATION_URL, +} from '@kbn/security-solution-plugin/common/api/detection_engine'; import { ELASTIC_SECURITY_RULE_ID } from '@kbn/security-solution-plugin/common/detection_engine/constants'; import type { PrePackagedRulesStatusResponse } from '@kbn/security-solution-plugin/public/detection_engine/rule_management/logic/types'; import { getPrebuiltRuleWithExceptionsMock } from '@kbn/security-solution-plugin/server/lib/detection_engine/prebuilt_rules/mocks'; @@ -34,10 +38,10 @@ export const SAMPLE_PREBUILT_RULE = createRuleAssetSavedObject({ * `createNewRuleAsset` to create mocked prebuilt rules and install only those * instead of all rules available in the `security_detection_engine` package */ -export const installAllPrebuiltRulesRequest = () => { - return cy.request({ +export const installAllPrebuiltRulesRequest = () => + cy.request({ method: 'POST', - url: 'internal/detection_engine/prebuilt_rules/installation/_perform', + url: PERFORM_RULE_INSTALLATION_URL, headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution', @@ -47,7 +51,6 @@ export const installAllPrebuiltRulesRequest = () => { mode: 'ALL_RULES', }, }); -}; export const getAvailablePrebuiltRulesCount = () => { cy.log('Get prebuilt rules count'); @@ -195,6 +198,24 @@ export const preventPrebuiltRulesPackageInstallation = () => { cy.intercept('POST', '/api/fleet/epm/packages/security_detection_engine/*', {}); }; +/** + * Install prebuilt rule assets. After installing these assets become available to be installed + * as prebuilt rules. Prebuilt rule assets can be generated via `createRuleAssetSavedObject()` helper function. + * + * It's also important to take into account that business logic tries to fetch prebuilt rules Fleet package + * and you need to add `preventPrebuiltRulesPackageInstallation()` to `beforeEach` section (before visit commands) + * to avoid actually pulling a real Fleet package and have only provided prebuilt rule assets for testing. + */ +export const installPrebuiltRuleAssets = (ruleAssets: Array): void => { + cy.log('Create mocked available to install prebuilt rules', ruleAssets.length); + preventPrebuiltRulesPackageInstallation(); + // TODO: use this bulk method once the issue with Cypress is fixed + // bulkCreateRuleAssets({ rules }); + ruleAssets.forEach((rule) => { + createNewRuleAsset({ rule }); + }); +}; + /** * Prevent the installation of the `security_detection_engine` package from Fleet. * The create a `security-rule` asset for each rule provided in the `rules` array. @@ -207,21 +228,16 @@ export const preventPrebuiltRulesPackageInstallation = () => { * * @param {string} installToKibana - Flag to decide whether to install the rules as 'alerts' SO. Defaults to true. */ export const createAndInstallMockedPrebuiltRules = ({ - rules, + rules: ruleAssets, installToKibana = true, }: { rules: Array; installToKibana?: boolean; }) => { - cy.log('Install prebuilt rules', rules?.length); - preventPrebuiltRulesPackageInstallation(); - // TODO: use this bulk method once the issue with Cypress is fixed - // bulkCreateRuleAssets({ rules }); - rules.forEach((rule) => { - createNewRuleAsset({ rule }); - }); + installPrebuiltRuleAssets(ruleAssets); if (installToKibana) { + cy.log('Install prebuilt rules', ruleAssets.length); return installAllPrebuiltRulesRequest(); } }; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/common.ts b/x-pack/test/security_solution_cypress/cypress/tasks/common.ts index ba4cd07cc4a51..a3bcf265455f5 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/common.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/common.ts @@ -9,6 +9,7 @@ import { DATA_VIEW_PATH, INITIAL_REST_VERSION } from '@kbn/data-views-plugin/ser import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; import { KIBANA_LOADING_ICON } from '../screens/security_header'; import { EUI_BASIC_TABLE_LOADING } from '../screens/common/controls'; +import { deleteAllDocuments } from './api_calls/elasticsearch'; const primaryButton = 0; @@ -134,17 +135,7 @@ export const deleteAlertsAndRules = () => { }, }); - rootRequest({ - method: 'POST', - url: `${Cypress.env( - 'ELASTICSEARCH_URL' - )}/.lists-*,.items-*,.alerts-security.alerts-*/_delete_by_query?conflicts=proceed&scroll_size=10000&refresh`, - body: { - query: { - match_all: {}, - }, - }, - }); + deleteAllDocuments('.lists-*,.items-*,.alerts-security.alerts-*'); }; export const deleteTimelines = () => { diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/create_new_rule.ts b/x-pack/test/security_solution_cypress/cypress/tasks/create_new_rule.ts index dabd0b89e4fb1..3286291ae325d 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/create_new_rule.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/create_new_rule.ts @@ -69,13 +69,11 @@ import { MITRE_TACTIC, QUERY_BAR, REFERENCE_URLS_INPUT, - REFRESH_BUTTON, RISK_MAPPING_OVERRIDE_OPTION, RISK_OVERRIDE, RULE_DESCRIPTION_INPUT, RULE_NAME_INPUT, RULE_NAME_OVERRIDE, - RULE_STATUS, RULE_TIMESTAMP_OVERRIDE, RULES_CREATION_FORM, RULES_CREATION_PREVIEW_BUTTON, @@ -696,16 +694,6 @@ export const waitForAlertsToPopulate = (alertCountThreshold = 1) => { waitForAlerts(); }; -export const waitForTheRuleToBeExecuted = () => { - cy.waitUntil(() => { - cy.get(REFRESH_BUTTON).click({ force: true }); - return cy - .get(RULE_STATUS) - .invoke('text') - .then((ruleStatus) => ruleStatus === 'succeeded'); - }); -}; - export const selectAndLoadSavedQuery = (queryName: string, queryValue: string) => { cy.get(QUERY_BAR).find(SHOW_QUERY_BAR_BUTTON).click(); diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/integrations.ts b/x-pack/test/security_solution_cypress/cypress/tasks/integrations.ts index e7c487971fba3..c7da263d42f42 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/integrations.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/integrations.ts @@ -5,23 +5,77 @@ * 2.0. */ +import { TypeOf } from '@kbn/config-schema'; import { - ADD_INTEGRATION_BTN, - INTEGRATION_ADDED_POP_UP, - QUEUE_URL, - SAVE_AND_CONTINUE_BTN, - SKIP_AGENT_INSTALLATION_BTN, -} from '../screens/integrations'; + AGENT_POLICY_API_ROUTES, + CreateAgentPolicyResponse, + EPM_API_ROUTES, + PACKAGE_POLICY_API_ROUTES, +} from '@kbn/fleet-plugin/common'; +import { + NewAgentPolicySchema, + SimplifiedCreatePackagePolicyRequestBodySchema, +} from '@kbn/fleet-plugin/server/types'; +import { rootRequest } from './common'; + +interface Package { + name: string; + version: string; +} -import { visit } from './login'; +export type AgentPolicy = TypeOf; +export type PackagePolicy = TypeOf; +export type PackagePolicyWithoutAgentPolicyId = Omit; + +/** + * Installs provided integrations by installing provided packages, creating an agent policy and adding a package policy. + * An agent policy is created with System integration enabled (with `?sys_monitoring=true` query param). + * + * Agent and package policies can be generated in Kibana by opening Fleet UI e.g. for AWS CloudFront the steps are following + * + * - open `app/integrations/detail/aws-1.17.0/overview?integration=cloudfront` + * - click the button `Add Amazon CloudFront` + * - fill in `Queue URL` + * - press `Preview API request` at the bottom + * - copy shown policies + */ +export function installIntegrations({ + packages, + agentPolicy, + packagePolicy, +}: { + packages: Package[]; + agentPolicy: AgentPolicy; + packagePolicy: Omit; +}): void { + // Bulk install provided packages + rootRequest({ + method: 'POST', + url: EPM_API_ROUTES.BULK_INSTALL_PATTERN, + body: { + packages, + force: true, + }, + headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' }, + }); -export const installAwsCloudFrontWithPolicy = () => { - visit('app/integrations/detail/aws-1.17.0/overview?integration=cloudfront'); - cy.get(ADD_INTEGRATION_BTN).click(); - cy.get(SKIP_AGENT_INSTALLATION_BTN).click(); - cy.get(QUEUE_URL).type('http://www.example.com'); + // Install agent and package policies + rootRequest({ + method: 'POST', + url: `${AGENT_POLICY_API_ROUTES.CREATE_PATTERN}?sys_monitoring=true`, + body: agentPolicy, + headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' }, + }).then((response) => { + const packagePolicyWithAgentPolicyId: PackagePolicy = { + ...packagePolicy, + policy_id: response.body.item.id, + }; - // Fleet installs an integration very slowly, so we have to increase the timeout here. - cy.get(SAVE_AND_CONTINUE_BTN).click(); - cy.get(INTEGRATION_ADDED_POP_UP, { timeout: 120000 }).should('exist'); -}; + rootRequest({ + method: 'POST', + url: PACKAGE_POLICY_API_ROUTES.CREATE_PATTERN, + body: packagePolicyWithAgentPolicyId, + headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' }, + }); + }); +} diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/rule_details.ts b/x-pack/test/security_solution_cypress/cypress/tasks/rule_details.ts index e40f8c375e83a..871cbcb82ab7a 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/rule_details.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/rule_details.ts @@ -6,6 +6,7 @@ */ import type { Exception } from '../objects/exception'; +import { PAGE_CONTENT_SPINNER } from '../screens/common/page'; import { RULE_STATUS } from '../screens/create_new_rule'; import { ADD_EXCEPTIONS_BTN_FROM_EMPTY_PROMPT_BTN, @@ -17,7 +18,7 @@ import { ALERTS_TAB, EXCEPTIONS_TAB, FIELDS_BROWSER_BTN, - REFRESH_BUTTON, + LAST_EXECUTION_STATUS_REFRESH_BUTTON, REMOVE_EXCEPTION_BTN, RULE_SWITCH, DEFINITION_DETAILS, @@ -31,6 +32,7 @@ import { BACK_TO_RULES_TABLE, EXCEPTIONS_TAB_EXPIRED_FILTER, EXCEPTIONS_TAB_ACTIVE_FILTER, + RULE_NAME_HEADER, } from '../screens/rule_details'; import { addExceptionConditions, @@ -114,10 +116,22 @@ export const removeException = () => { cy.get(REMOVE_EXCEPTION_BTN).click(); }; +/** + * Waits for rule details page to be loaded + * + * @param ruleName rule's name + */ +export const waitForPageToBeLoaded = (ruleName: string): void => { + cy.get(PAGE_CONTENT_SPINNER).should('be.visible'); + cy.contains(RULE_NAME_HEADER, ruleName).should('be.visible'); + cy.get(PAGE_CONTENT_SPINNER).should('not.exist'); +}; + export const waitForTheRuleToBeExecuted = () => { cy.waitUntil(() => { - cy.log('Wating for the rule to be executed'); - cy.get(REFRESH_BUTTON).click({ force: true }); + cy.log('Waiting for the rule to be executed'); + cy.get(LAST_EXECUTION_STATUS_REFRESH_BUTTON).click(); + return cy .get(RULE_STATUS) .invoke('text') diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/rule_details_flyout.ts b/x-pack/test/security_solution_cypress/cypress/tasks/rule_details_flyout.ts new file mode 100644 index 0000000000000..b684ccf6cdfe7 --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/tasks/rule_details_flyout.ts @@ -0,0 +1,16 @@ +/* + * 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 { FILTER_INPUT, TABLE_TAB } from '../screens/rule_details_flyout'; + +export const openTable = (): void => { + cy.get(TABLE_TAB).click(); +}; + +export const filterBy = (value: string): void => { + cy.get(FILTER_INPUT).type(value); +}; diff --git a/x-pack/test/security_solution_cypress/cypress/tsconfig.json b/x-pack/test/security_solution_cypress/cypress/tsconfig.json index b412c9d3acef5..ff1d9c7551f75 100644 --- a/x-pack/test/security_solution_cypress/cypress/tsconfig.json +++ b/x-pack/test/security_solution_cypress/cypress/tsconfig.json @@ -45,5 +45,6 @@ "@kbn/security-solution-plugin", "@kbn/dev-utils", "@kbn/expandable-flyout", + "@kbn/config-schema", ] }