From f8121b897dd65b14366a0958b7fc41bd07a91cb5 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 18 Aug 2023 15:10:11 -0400 Subject: [PATCH] [8.10] Fix osquery cypress tests (#163988) (#164248) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Backport This will backport the following commits from `main` to `8.10`: - [Fix osquery cypress tests (#163988)](https://github.com/elastic/kibana/pull/163988) ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) Co-authored-by: Patryk Kopyciński --- .../pull_request/osquery_cypress.yml | 10 + .../pipelines/pull_request/pipeline.ts | 4 +- .../steps/functional/osquery_cypress_burn.sh | 17 + x-pack/plugins/osquery/cypress.config.ts | 2 +- .../osquery/cypress/e2e/all/alerts.cy.ts | 721 -------------- .../cypress/e2e/all/alerts_cases.cy.ts | 141 +++ .../cypress/e2e/all/alerts_liked_apps.cy.ts | 188 ++++ .../e2e/all/alerts_multiple_agents.cy.ts | 106 ++ .../all/alerts_response_actions_form.cy.ts | 188 ++++ .../osquery/cypress/e2e/all/packs.cy.ts | 934 ------------------ .../cypress/e2e/all/packs_create_edit.cy.ts | 628 ++++++++++++ .../cypress/e2e/all/packs_integration.cy.ts | 319 ++++++ .../cypress/e2e/roles/alert_test.cy.ts | 1 + .../osquery/cypress/e2e/roles/none.cy.ts | 3 +- x-pack/plugins/osquery/cypress/support/e2e.ts | 4 +- .../osquery/cypress/tasks/api_fixtures.ts | 1 + .../osquery/cypress/tasks/live_query.ts | 2 + x-pack/plugins/osquery/package.json | 6 +- .../scripts/endpoint/common/fleet_services.ts | 11 +- .../scripts/run_cypress/parallel.ts | 4 + 20 files changed, 1628 insertions(+), 1662 deletions(-) create mode 100755 .buildkite/scripts/steps/functional/osquery_cypress_burn.sh delete mode 100644 x-pack/plugins/osquery/cypress/e2e/all/alerts.cy.ts create mode 100644 x-pack/plugins/osquery/cypress/e2e/all/alerts_cases.cy.ts create mode 100644 x-pack/plugins/osquery/cypress/e2e/all/alerts_liked_apps.cy.ts create mode 100644 x-pack/plugins/osquery/cypress/e2e/all/alerts_multiple_agents.cy.ts create mode 100644 x-pack/plugins/osquery/cypress/e2e/all/alerts_response_actions_form.cy.ts delete mode 100644 x-pack/plugins/osquery/cypress/e2e/all/packs.cy.ts create mode 100644 x-pack/plugins/osquery/cypress/e2e/all/packs_create_edit.cy.ts create mode 100644 x-pack/plugins/osquery/cypress/e2e/all/packs_integration.cy.ts diff --git a/.buildkite/pipelines/pull_request/osquery_cypress.yml b/.buildkite/pipelines/pull_request/osquery_cypress.yml index 4eefffdf7ee06..9d7c399e87253 100644 --- a/.buildkite/pipelines/pull_request/osquery_cypress.yml +++ b/.buildkite/pipelines/pull_request/osquery_cypress.yml @@ -12,3 +12,13 @@ steps: limit: 1 artifact_paths: - "target/kibana-osquery/**/*" + + - command: .buildkite/scripts/steps/functional/osquery_cypress_burn.sh + label: 'Osquery Cypress Tests, burning changed specs' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 50 + soft_fail: true + artifact_paths: + - "target/kibana-osquery/**/*" diff --git a/.buildkite/scripts/pipelines/pull_request/pipeline.ts b/.buildkite/scripts/pipelines/pull_request/pipeline.ts index 3190f5650b2e0..1eac46daf408d 100644 --- a/.buildkite/scripts/pipelines/pull_request/pipeline.ts +++ b/.buildkite/scripts/pipelines/pull_request/pipeline.ts @@ -80,6 +80,7 @@ const uploadPipeline = (pipelineContent: string | object) => { getPipeline('.buildkite/pipelines/pull_request/security_solution_explore.yml') ); pipeline.push(getPipeline('.buildkite/pipelines/pull_request/defend_workflows.yml')); + pipeline.push(getPipeline('.buildkite/pipelines/pull_request/osquery_cypress.yml')); } if ( @@ -200,7 +201,8 @@ const uploadPipeline = (pipelineContent: string | object) => { pipeline.push(getPipeline('.buildkite/pipelines/pull_request/post_build.yml')); - uploadPipeline(pipeline.join('\n')); + // remove duplicated steps + uploadPipeline([...new Set(pipeline)].join('\n')); } catch (ex) { console.error('PR pipeline generation error', ex.message); process.exit(1); diff --git a/.buildkite/scripts/steps/functional/osquery_cypress_burn.sh b/.buildkite/scripts/steps/functional/osquery_cypress_burn.sh new file mode 100755 index 0000000000000..b7fd648e53939 --- /dev/null +++ b/.buildkite/scripts/steps/functional/osquery_cypress_burn.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source .buildkite/scripts/common/util.sh +source .buildkite/scripts/steps/functional/common_cypress.sh + +.buildkite/scripts/bootstrap.sh +node scripts/build_kibana_platform_plugins.js + +export JOB=kibana-osquery-cypress + +buildkite-agent meta-data set "${BUILDKITE_JOB_ID}_is_test_execution_step" 'false' + +echo "--- Osquery Cypress tests, burning changed specs (Chrome)" + +yarn --cwd x-pack/plugins/osquery cypress:changed-specs-only diff --git a/x-pack/plugins/osquery/cypress.config.ts b/x-pack/plugins/osquery/cypress.config.ts index 62b4cc2533113..ba126f71343df 100644 --- a/x-pack/plugins/osquery/cypress.config.ts +++ b/x-pack/plugins/osquery/cypress.config.ts @@ -36,6 +36,6 @@ export default defineCypressConfig({ baseUrl: 'http://localhost:5601', experimentalRunAllSpecs: true, experimentalMemoryManagement: true, - numTestsKeptInMemory: 10, + numTestsKeptInMemory: 3, }, }); diff --git a/x-pack/plugins/osquery/cypress/e2e/all/alerts.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/alerts.cy.ts deleted file mode 100644 index f0956b960a7f5..0000000000000 --- a/x-pack/plugins/osquery/cypress/e2e/all/alerts.cy.ts +++ /dev/null @@ -1,721 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - cleanupCase, - cleanupPack, - cleanupRule, - loadCase, - loadPack, - loadRule, - multiQueryPackFixture, - packFixture, -} from '../../tasks/api_fixtures'; -import { - RESPONSE_ACTIONS_ITEM_0, - RESPONSE_ACTIONS_ITEM_1, - RESPONSE_ACTIONS_ITEM_2, - OSQUERY_RESPONSE_ACTION_ADD_BUTTON, -} from '../../tasks/response_actions'; -import { ROLE, login } from '../../tasks/login'; -import { - addToCase, - checkActionItemsInResults, - findAndClickButton, - findFormFieldByRowsLabelAndType, - inputQuery, - loadRuleAlerts, - submitQuery, - takeOsqueryActionWithParams, - toggleRuleOffAndOn, - typeInECSFieldInput, - viewRecentCaseAndCheckResults, -} from '../../tasks/live_query'; -import { preparePack } from '../../tasks/packs'; -import { - closeDateTabIfVisible, - closeModalIfVisible, - closeToastIfVisible, - generateRandomStringName, - interceptCaseId, -} from '../../tasks/integrations'; -import { navigateTo } from '../../tasks/navigation'; -import { RESULTS_TABLE, RESULTS_TABLE_BUTTON } from '../../screens/live_query'; -import { OSQUERY_POLICY } from '../../screens/fleet'; - -const UUID_REGEX = '[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}'; - -describe('Alert Event Details', () => { - beforeEach(() => { - login(ROLE.soc_manager); - }); - - describe('Packs and rules creation', () => { - let ruleId: string; - let ruleName: string; - let packId: string; - let packName: string; - - const packData = packFixture(); - - before(() => { - loadPack(packData).then((data) => { - packId = data.saved_object_id; - packName = data.name; - }); - loadRule().then((data) => { - ruleId = data.id; - ruleName = data.name; - }); - }); - after(() => { - cleanupPack(packId); - cleanupRule(ruleId); - }); - - it('should prepare packs and alert rules', () => { - navigateTo('/app/osquery/live_queries'); - preparePack(packName); - findAndClickButton('Edit'); - cy.contains(`Edit ${packName}`); - findFormFieldByRowsLabelAndType( - 'Scheduled agent policies (optional)', - `${OSQUERY_POLICY}{downArrow}{enter}` - ); - findAndClickButton('Update pack'); - closeModalIfVisible(); - cy.contains(`Successfully updated "${packName}" pack`); - closeToastIfVisible(); - - toggleRuleOffAndOn(ruleName); - }); - }); - - describe.skip('Response actions', () => { - let multiQueryPackId: string; - let multiQueryPackName: string; - let ruleId: string; - let ruleName: string; - let packId: string; - let packName: string; - const packData = packFixture(); - const multiQueryPackData = multiQueryPackFixture(); - - beforeEach(() => { - loadPack(packData).then((data) => { - packId = data.saved_object_id; - packName = data.name; - }); - loadPack(multiQueryPackData).then((data) => { - multiQueryPackId = data.saved_object_id; - multiQueryPackName = data.name; - }); - loadRule().then((data) => { - ruleId = data.id; - ruleName = data.name; - }); - }); - afterEach(() => { - cleanupPack(packId); - cleanupPack(multiQueryPackId); - cleanupRule(ruleId); - }); - - it('adds response actions with osquery with proper validation and form values', () => { - cy.visit('/app/security/rules'); - cy.contains(ruleName).click(); - cy.getBySel('editRuleSettingsLink').click(); - cy.getBySel('globalLoadingIndicator').should('not.exist'); - closeDateTabIfVisible(); - cy.getBySel('edit-rule-actions-tab').click(); - cy.contains('Response actions are run on each rule execution.'); - cy.getBySel(OSQUERY_RESPONSE_ACTION_ADD_BUTTON).click(); - cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { - cy.contains('Query is a required field'); - inputQuery('select * from uptime1'); - }); - cy.getBySel(OSQUERY_RESPONSE_ACTION_ADD_BUTTON).click(); - cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { - cy.contains('Run a set of queries in a pack').click(); - }); - cy.contains('Save changes').click(); - cy.getBySel('response-actions-error') - .within(() => { - cy.contains('Pack is a required field'); - }) - .should('exist'); - cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { - cy.contains('Pack is a required field'); - cy.getBySel('comboBoxInput').type(`${packName}{downArrow}{enter}`); - }); - - cy.getBySel(OSQUERY_RESPONSE_ACTION_ADD_BUTTON).click(); - - cy.getBySel(RESPONSE_ACTIONS_ITEM_2).within(() => { - cy.contains('Query is a required field'); - inputQuery('select * from uptime'); - cy.contains('Advanced').click(); - typeInECSFieldInput('message{downArrow}{enter}'); - cy.getBySel('osqueryColumnValueSelect').type('days{downArrow}{enter}'); - cy.wait(1000); // wait for the validation to trigger - cypress is way faster than users ;) - }); - - cy.getBySel('ruleEditSubmitButton').click(); - cy.contains(`${ruleName} was saved`).should('exist'); - closeToastIfVisible(); - - cy.getBySel('editRuleSettingsLink').click(); - cy.getBySel('globalLoadingIndicator').should('not.exist'); - cy.getBySel('edit-rule-actions-tab').click(); - cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { - cy.contains('select * from uptime1'); - }); - cy.getBySel(RESPONSE_ACTIONS_ITEM_2).within(() => { - cy.contains('select * from uptime'); - cy.contains('Log message optimized for viewing in a log viewer'); - cy.contains('Days of uptime'); - }); - cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { - cy.contains(packName); - cy.getBySel('comboBoxInput').type('{backspace}{enter}'); - }); - cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { - cy.contains('select * from uptime1'); - cy.getBySel('remove-response-action').click(); - }); - cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { - cy.contains('Search for a pack to run'); - cy.contains('Pack is a required field'); - cy.getBySel('comboBoxInput').type(`${packName}{downArrow}{enter}`); - }); - cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { - cy.contains('select * from uptime'); - cy.contains('Log message optimized for viewing in a log viewer'); - cy.contains('Days of uptime'); - }); - cy.intercept('PUT', '/api/detection_engine/rules').as('saveRuleSingleQuery'); - cy.getBySel('ruleEditSubmitButton').click(); - cy.wait('@saveRuleSingleQuery').should(({ request }) => { - const oneQuery = [ - { - interval: 3600, - query: 'select * from uptime;', - id: Object.keys(packData.queries)[0], - }, - ]; - expect(request.body.response_actions[0].params.queries).to.deep.equal(oneQuery); - }); - - cy.contains(`${ruleName} was saved`).should('exist'); - closeToastIfVisible(); - - cy.getBySel('editRuleSettingsLink').click(); - cy.getBySel('globalLoadingIndicator').should('not.exist'); - cy.getBySel('edit-rule-actions-tab').click(); - cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { - cy.contains(packName); - cy.getBySel('comboBoxInput').type(`${multiQueryPackName}{downArrow}{enter}`); - checkActionItemsInResults({ - cases: false, - lens: false, - discover: false, - timeline: false, - }); - }); - cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { - cy.contains('select * from uptime'); - cy.contains('Log message optimized for viewing in a log viewer'); - cy.contains('Days of uptime'); - }); - cy.intercept('PUT', '/api/detection_engine/rules').as('saveRuleMultiQuery'); - - cy.contains('Save changes').click(); - cy.wait('@saveRuleMultiQuery').should(({ request }) => { - const threeQueries = [ - { - interval: 3600, - query: 'SELECT * FROM memory_info;', - platform: 'linux', - id: Object.keys(multiQueryPackData.queries)[0], - }, - { - interval: 3600, - query: 'SELECT * FROM system_info;', - id: Object.keys(multiQueryPackData.queries)[1], - }, - { - interval: 10, - query: 'select opera_extensions.* from users join opera_extensions using (uid);', - id: Object.keys(multiQueryPackData.queries)[2], - }, - ]; - expect(request.body.response_actions[0].params.queries).to.deep.equal(threeQueries); - }); - }); - }); - - describe('investigation guide', () => { - let ruleId: string; - let ruleName: string; - - before(() => { - loadRule().then((data) => { - ruleId = data.id; - ruleName = data.name; - }); - }); - - after(() => { - cleanupRule(ruleId); - }); - - it('should be able to add investigation guides to response actions', () => { - const investigationGuideNote = - 'You have queries in the investigation guide. Add them as response actions?'; - cy.visit('/app/security/rules'); - cy.contains(ruleName).click(); - cy.getBySel('editRuleSettingsLink').click(); - cy.getBySel('globalLoadingIndicator').should('not.exist'); - cy.getBySel('edit-rule-actions-tab').click(); - - cy.contains(investigationGuideNote); - cy.getBySel('osqueryAddInvestigationGuideQueries').click(); - cy.contains(investigationGuideNote).should('not.exist'); - - cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { - cy.contains("SELECT * FROM os_version where name='{{host.os.name}}';"); - cy.contains('host.os.platform'); - cy.contains('platform'); - }); - cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { - cy.contains('select * from users'); - }); - cy.contains('Save changes').click(); - cy.contains(`${ruleName} was saved`).should('exist'); - closeToastIfVisible(); - }); - }); - - describe('timeline', () => { - let ruleId: string; - let ruleName: string; - - before(() => { - loadRule().then((data) => { - ruleId = data.id; - ruleName = data.name; - }); - }); - after(() => { - cleanupRule(ruleId); - }); - it('should be able to run live query and add to timeline (-depending on the previous test)', () => { - const TIMELINE_NAME = 'Untitled timeline'; - loadRuleAlerts(ruleName); - cy.getBySel('timeline-context-menu-button').first().click(); - cy.contains('Run Osquery'); - cy.getBySel('expand-event').first().click(); - cy.getBySel('take-action-dropdown-btn').click(); - cy.getBySel('osquery-action-item').click(); - cy.contains('1 agent selected.'); - inputQuery('select * from uptime;'); - submitQuery(); - cy.contains('Results'); - cy.contains('Add to timeline investigation'); - cy.contains('Save for later').click(); - cy.contains('Save query'); - cy.get('[data-test-subj="osquery-save-query-flyout"]').within(() => { - cy.get('.euiButtonEmpty').contains('Cancel').click(); - }); - cy.getBySel('add-to-timeline').first().click(); - cy.getBySel('globalToastList').contains('Added'); - closeToastIfVisible(); - cy.getBySel(RESULTS_TABLE).within(() => { - cy.getBySel(RESULTS_TABLE_BUTTON).should('not.exist'); - }); - cy.contains('Cancel').click(); - cy.getBySel('flyoutBottomBar').within(() => { - cy.contains(TIMELINE_NAME).click(); - }); - cy.getBySel('draggableWrapperKeyboardHandler').contains('action_id: "'); - // timeline unsaved changes modal - cy.visit('/app/osquery'); - closeModalIfVisible(); - }); - }); - - describe('substitute params', () => { - let ruleId: string; - let ruleName: string; - - before(() => { - loadRule().then((data) => { - ruleId = data.id; - ruleName = data.name; - }); - }); - after(() => { - cleanupRule(ruleId); - }); - - it('should substitute parameters in investigation guide', () => { - loadRuleAlerts(ruleName); - cy.getBySel('expand-event').first().click(); - cy.contains('Get processes').click(); - cy.getBySel('flyout-body-osquery').within(() => { - cy.contains("SELECT * FROM os_version where name='Ubuntu';"); - cy.contains('host.os.platform'); - cy.contains('platform'); - }); - }); - }); - - describe.skip('Case creation', () => { - let ruleId: string; - let ruleName: string; - let packId: string; - let packName: string; - let caseId: string; - const packData = packFixture(); - - before(() => { - loadPack(packData).then((data) => { - packId = data.saved_object_id; - packName = data.name; - }); - loadRule(true).then((data) => { - ruleId = data.id; - ruleName = data.name; - }); - interceptCaseId((id) => { - caseId = id; - }); - }); - - after(() => { - cleanupPack(packId); - cleanupRule(ruleId); - cleanupCase(caseId); - }); - - it('runs osquery against alert and creates a new case', () => { - const [caseName, caseDescription] = generateRandomStringName(2); - loadRuleAlerts(ruleName); - cy.getBySel('expand-event').first().click({ force: true }); - cy.getBySel('take-action-dropdown-btn').click(); - cy.getBySel('osquery-action-item').click(); - cy.contains('Run a set of queries in a pack').wait(500).click(); - cy.getBySel('select-live-pack').within(() => { - cy.getBySel('comboBoxInput').type(`${packName}{downArrow}{enter}`); - }); - submitQuery(); - cy.get('[aria-label="Add to Case"]').first().click(); - cy.getBySel('cases-table-add-case-filter-bar').click(); - cy.getBySel('create-case-flyout').should('be.visible'); - cy.getBySel('caseTitle').within(() => { - cy.getBySel('input').type(caseName); - }); - cy.getBySel('caseDescription').within(() => { - cy.getBySel('euiMarkdownEditorTextArea').type(caseDescription); - }); - cy.getBySel('create-case-submit').click(); - cy.contains(`An alert was added to "${caseName}"`); - }); - }); - - describe('Case', () => { - let ruleId: string; - let ruleName: string; - let caseId: string; - - before(() => { - loadRule(true).then((data) => { - ruleId = data.id; - ruleName = data.name; - }); - loadCase('securitySolution').then((data) => { - caseId = data.id; - }); - }); - - after(() => { - cleanupRule(ruleId); - cleanupCase(caseId); - }); - - it('sees osquery results from last action and add to a case', () => { - loadRuleAlerts(ruleName); - cy.getBySel('expand-event').first().click(); - cy.getBySel('responseActionsViewTab').click(); - cy.getBySel('responseActionsViewWrapper').should('exist'); - cy.contains('select * from users;'); - cy.contains("SELECT * FROM os_version where name='Ubuntu';"); - cy.getBySel('osquery-results-comment').each(($comment) => { - cy.wrap($comment).within(() => { - // On initial load result table might not render due to displayed error - if ($comment.find('div .euiDataGridRow').length <= 0) { - // If tabs are present try clicking between status and results to get rid of the error message - if ($comment.find('div .euiTabs').length > 0) { - cy.getBySel('osquery-status-tab').click(); - cy.getBySel('osquery-results-tab').click(); - cy.getBySel('dataGridRowCell', { timeout: 120000 }).should('have.lengthOf.above', 0); - } - } else { - // Result tab was rendered successfully - cy.getBySel('dataGridRowCell', { timeout: 120000 }).should('have.lengthOf.above', 0); - } - // } - }); - }); - checkActionItemsInResults({ - lens: true, - discover: true, - cases: true, - timeline: true, - }); - addToCase(caseId); - viewRecentCaseAndCheckResults(); - }); - }); - - describe('Discover', () => { - let ruleId: string; - let ruleName: string; - - before(() => { - loadRule(true).then((data) => { - ruleId = data.id; - ruleName = data.name; - }); - }); - after(() => { - cleanupRule(ruleId); - }); - - it('can visit discover from response action results', () => { - const discoverRegex = new RegExp(`action_id: ${UUID_REGEX}`); - loadRuleAlerts(ruleName); - cy.getBySel('expand-event').first().click(); - cy.getBySel('responseActionsViewTab').click(); - cy.getBySel('responseActionsViewWrapper').should('exist'); - checkActionItemsInResults({ - lens: true, - discover: true, - cases: true, - timeline: true, - }); - cy.contains('View in Discover') - .should('exist') - .should('have.attr', 'href') - .then(($href) => { - // @ts-expect-error-next-line href string - check types - cy.visit($href); - cy.getBySel('breadcrumbs').contains('Discover').should('exist'); - cy.getBySel('discoverDocTable', { timeout: 60000 }).within(() => { - cy.contains(`action_data.query`); - }); - cy.contains(discoverRegex); - }); - }); - }); - - describe('Lens', () => { - let ruleId: string; - let ruleName: string; - - before(() => { - loadRule(true).then((data) => { - ruleId = data.id; - ruleName = data.name; - }); - }); - after(() => { - cleanupRule(ruleId); - }); - - it('can visit lens from response action results', () => { - const lensRegex = new RegExp(`Action ${UUID_REGEX} results`); - loadRuleAlerts(ruleName); - cy.getBySel('expand-event').first().click(); - cy.getBySel('responseActionsViewTab').click(); - cy.getBySel('responseActionsViewWrapper').should('exist'); - checkActionItemsInResults({ - lens: true, - discover: true, - cases: true, - timeline: true, - }); - cy.getBySel('osquery-results-comment') - .first() - .within(() => { - let lensUrl = ''; - cy.window().then((win) => { - cy.stub(win, 'open') - .as('windowOpen') - .callsFake((url) => { - lensUrl = url; - }); - }); - cy.get(`[aria-label="View in Lens"]`).click(); - cy.window() - .its('open') - .then(() => { - cy.visit(lensUrl); - }); - }); - cy.getBySel('lnsWorkspace').should('exist'); - cy.getBySel('breadcrumbs').contains(lensRegex); - }); - }); - - describe('Timeline', () => { - let ruleId: string; - let ruleName: string; - - before(() => { - loadRule(true).then((data) => { - ruleId = data.id; - ruleName = data.name; - }); - }); - after(() => { - cleanupRule(ruleId); - }); - - it('can add to timeline from response action results', () => { - const timelineRegex = new RegExp(`Added ${UUID_REGEX} to timeline`); - const filterRegex = new RegExp(`action_id: "${UUID_REGEX}"`); - loadRuleAlerts(ruleName); - cy.getBySel('expand-event').first().click(); - cy.getBySel('responseActionsViewTab').click(); - cy.getBySel('responseActionsViewWrapper').should('exist'); - checkActionItemsInResults({ - lens: true, - discover: true, - cases: true, - timeline: true, - }); - cy.getBySel('osquery-results-comment') - .first() - .within(() => { - cy.get('.euiTableRow') - .first() - .within(() => { - cy.getBySel('add-to-timeline').click(); - }); - }); - cy.contains(timelineRegex); - cy.getBySel('flyoutBottomBar').contains('Untitled timeline').click(); - cy.contains(filterRegex); - }); - }); - - describe('Params', () => { - let ruleId: string; - let ruleName: string; - - before(() => { - loadRule(true).then((data) => { - ruleId = data.id; - ruleName = data.name; - }); - }); - - after(() => { - cleanupRule(ruleId); - }); - - it('should substitute parameters in live query and increase number of ran queries', () => { - let initialNotificationCount: number; - let updatedNotificationCount: number; - loadRuleAlerts(ruleName); - cy.getBySel('expand-event').first().click(); - cy.getBySel('response-actions-notification') - .should('not.have.text', '0') - .then((element) => { - initialNotificationCount = parseInt(element.text(), 10); - }); - takeOsqueryActionWithParams(); - cy.getBySel('osquery-empty-button').click(); - cy.getBySel('response-actions-notification') - .should('not.have.text', '0') - .then((element) => { - updatedNotificationCount = parseInt(element.text(), 10); - expect(initialNotificationCount).to.be.equal(updatedNotificationCount - 1); - }) - .then(() => { - cy.getBySel('responseActionsViewTab').click(); - cy.getBySel('responseActionsViewWrapper').within(() => { - cy.contains('tags'); - cy.getBySel('osquery-results-comment').should('have.length', updatedNotificationCount); - }); - }); - }); - }); - - describe('Multiple agents', () => { - let ruleId: string; - let ruleName: string; - - before(() => { - loadRule(true).then((data) => { - ruleId = data.id; - ruleName = data.name; - }); - }); - - after(() => { - cleanupRule(ruleId); - }); - - it('should be able to run take action query against all enrolled agents', () => { - loadRuleAlerts(ruleName); - cy.getBySel('expand-event').first().click(); - cy.getBySel('take-action-dropdown-btn').click(); - cy.getBySel('osquery-action-item').click(); - cy.getBySel('agentSelection').within(() => { - cy.getBySel('comboBoxClearButton').click(); - cy.getBySel('comboBoxInput').type('All{downArrow}{enter}{esc}'); - cy.contains('All agents'); - }); - inputQuery("SELECT * FROM os_version where name='{{host.os.name}}';", { - parseSpecialCharSequences: false, - }); - cy.wait(1000); - submitQuery(); - cy.getBySel('flyout-body-osquery').within(() => { - // at least 2 agents should have responded, sometimes it takes a while for the agents to respond - cy.get('[data-grid-row-index]', { timeout: 6000000 }).should('have.length.at.least', 2); - }); - }); - }); - - describe('Params in timeline', () => { - let ruleId: string; - let ruleName: string; - - before(() => { - loadRule(true).then((data) => { - ruleId = data.id; - ruleName = data.name; - }); - }); - - after(() => { - cleanupRule(ruleId); - }); - - it('should substitute params in osquery ran from timelines alerts', () => { - loadRuleAlerts(ruleName); - cy.getBySel('send-alert-to-timeline-button').first().click(); - cy.getBySel('query-events-table').within(() => { - cy.getBySel('expand-event').first().click(); - }); - takeOsqueryActionWithParams(); - }); - }); -}); diff --git a/x-pack/plugins/osquery/cypress/e2e/all/alerts_cases.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/alerts_cases.cy.ts new file mode 100644 index 0000000000000..9d3c3ea482a08 --- /dev/null +++ b/x-pack/plugins/osquery/cypress/e2e/all/alerts_cases.cy.ts @@ -0,0 +1,141 @@ +/* + * 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 { + cleanupCase, + cleanupPack, + cleanupRule, + loadCase, + loadPack, + loadRule, + packFixture, +} from '../../tasks/api_fixtures'; +import { ROLE, login } from '../../tasks/login'; +import { + addToCase, + checkActionItemsInResults, + loadRuleAlerts, + submitQuery, + viewRecentCaseAndCheckResults, +} from '../../tasks/live_query'; +import { generateRandomStringName, interceptCaseId } from '../../tasks/integrations'; + +describe('Alert Event Details - Cases', () => { + let ruleId: string; + let ruleName: string; + let packId: string; + let packName: string; + const packData = packFixture(); + + before(() => { + loadPack(packData).then((data) => { + packId = data.saved_object_id; + packName = data.name; + }); + loadRule(true).then((data) => { + ruleId = data.id; + ruleName = data.name; + loadRuleAlerts(data.name); + }); + }); + + beforeEach(() => { + login(ROLE.soc_manager); + cy.visit('/app/security/rules'); + cy.contains(ruleName).click(); + }); + + after(() => { + cleanupPack(packId); + cleanupRule(ruleId); + }); + + describe('Case creation', () => { + let caseId: string; + + before(() => { + interceptCaseId((id) => { + caseId = id; + }); + }); + + after(() => { + cleanupCase(caseId); + }); + + it('runs osquery against alert and creates a new case', () => { + const [caseName, caseDescription] = generateRandomStringName(2); + cy.getBySel('expand-event').first().click({ force: true }); + cy.getBySel('take-action-dropdown-btn').click(); + cy.getBySel('osquery-action-item').click(); + cy.contains('Run a set of queries in a pack').wait(500).click(); + cy.getBySel('select-live-pack').within(() => { + cy.getBySel('comboBoxInput').type(`${packName}{downArrow}{enter}`); + }); + submitQuery(); + cy.get('[aria-label="Add to Case"]').first().click(); + cy.getBySel('cases-table-add-case-filter-bar').click(); + cy.getBySel('create-case-flyout').should('be.visible'); + cy.getBySel('caseTitle').within(() => { + cy.getBySel('input').type(caseName); + }); + cy.getBySel('caseDescription').within(() => { + cy.getBySel('euiMarkdownEditorTextArea').type(caseDescription); + }); + cy.getBySel('create-case-submit').click(); + cy.contains(`An alert was added to "${caseName}"`); + }); + }); + + describe('Case', () => { + let caseId: string; + + before(() => { + loadCase('securitySolution').then((data) => { + caseId = data.id; + }); + }); + + after(() => { + cleanupCase(caseId); + }); + + it('sees osquery results from last action and add to a case', () => { + cy.getBySel('expand-event').first().click(); + cy.getBySel('securitySolutionDocumentDetailsFlyoutResponseSectionHeader').click(); + cy.getBySel('securitySolutionDocumentDetailsFlyoutResponseButton').click(); + cy.getBySel('responseActionsViewWrapper').should('exist'); + cy.contains('select * from users;'); + cy.contains("SELECT * FROM os_version where name='Ubuntu';"); + cy.getBySel('osquery-results-comment').each(($comment) => { + cy.wrap($comment).within(() => { + // On initial load result table might not render due to displayed error + if ($comment.find('div .euiDataGridRow').length <= 0) { + // If tabs are present try clicking between status and results to get rid of the error message + if ($comment.find('div .euiTabs').length > 0) { + cy.getBySel('osquery-status-tab').click(); + cy.getBySel('osquery-results-tab').click(); + cy.getBySel('dataGridRowCell', { timeout: 120000 }).should('have.lengthOf.above', 0); + } + } else { + // Result tab was rendered successfully + cy.getBySel('dataGridRowCell', { timeout: 120000 }).should('have.lengthOf.above', 0); + } + // } + }); + }); + checkActionItemsInResults({ + lens: true, + discover: true, + cases: true, + timeline: true, + }); + addToCase(caseId); + viewRecentCaseAndCheckResults(); + }); + }); +}); diff --git a/x-pack/plugins/osquery/cypress/e2e/all/alerts_liked_apps.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/alerts_liked_apps.cy.ts new file mode 100644 index 0000000000000..6ccff4840d893 --- /dev/null +++ b/x-pack/plugins/osquery/cypress/e2e/all/alerts_liked_apps.cy.ts @@ -0,0 +1,188 @@ +/* + * 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 { cleanupRule, loadRule } from '../../tasks/api_fixtures'; +import { RESPONSE_ACTIONS_ITEM_0, RESPONSE_ACTIONS_ITEM_1 } from '../../tasks/response_actions'; +import { ROLE, login } from '../../tasks/login'; +import { + checkActionItemsInResults, + inputQuery, + loadRuleAlerts, + submitQuery, +} from '../../tasks/live_query'; +import { closeModalIfVisible, closeToastIfVisible } from '../../tasks/integrations'; +import { RESULTS_TABLE, RESULTS_TABLE_BUTTON } from '../../screens/live_query'; + +const UUID_REGEX = '[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}'; + +describe('Alert Event Details', { browser: 'electron' }, () => { + let ruleId: string; + let ruleName: string; + + before(() => { + loadRule().then((data) => { + ruleId = data.id; + ruleName = data.name; + loadRuleAlerts(data.name); + }); + }); + + after(() => { + cleanupRule(ruleId); + }); + + beforeEach(() => { + login(ROLE.soc_manager); + cy.visit('/app/security/rules'); + cy.contains(ruleName).click(); + }); + + it('should be able to add investigation guides to response actions', () => { + const investigationGuideNote = + 'You have queries in the investigation guide. Add them as response actions?'; + cy.getBySel('editRuleSettingsLink').click(); + cy.getBySel('globalLoadingIndicator').should('not.exist'); + cy.getBySel('edit-rule-actions-tab').click(); + + cy.contains(investigationGuideNote); + cy.getBySel('osqueryAddInvestigationGuideQueries').click(); + cy.contains(investigationGuideNote).should('not.exist'); + + cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { + cy.contains("SELECT * FROM os_version where name='{{host.os.name}}';"); + cy.contains('host.os.platform'); + cy.contains('platform'); + }); + cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { + cy.contains('select * from users'); + }); + cy.contains('Save changes').click(); + cy.contains(`${ruleName} was saved`).should('exist'); + closeToastIfVisible(); + }); + + it('should be able to run live query and add to timeline', () => { + const TIMELINE_NAME = 'Untitled timeline'; + cy.getBySel('timeline-context-menu-button').first().click(); + cy.contains('Run Osquery'); + cy.getBySel('expand-event').first().click(); + cy.getBySel('take-action-dropdown-btn').click(); + cy.getBySel('osquery-action-item').click(); + cy.contains('1 agent selected.'); + inputQuery('select * from uptime;'); + submitQuery(); + cy.contains('Results'); + cy.contains('Add to timeline investigation'); + cy.contains('Save for later').click(); + cy.contains('Save query'); + cy.get('[data-test-subj="osquery-save-query-flyout"]').within(() => { + cy.get('.euiButtonEmpty').contains('Cancel').click(); + }); + cy.getBySel('add-to-timeline').first().click(); + cy.getBySel('globalToastList').contains('Added'); + closeToastIfVisible(); + cy.getBySel(RESULTS_TABLE).within(() => { + cy.getBySel(RESULTS_TABLE_BUTTON).should('not.exist'); + }); + cy.contains('Cancel').click(); + cy.getBySel('flyoutBottomBar').within(() => { + cy.contains(TIMELINE_NAME).click(); + }); + cy.getBySel('draggableWrapperKeyboardHandler').contains('action_id: "'); + // timeline unsaved changes modal + cy.visit('/app/osquery'); + closeModalIfVisible(); + }); + + it('can visit discover from response action results', () => { + const discoverRegex = new RegExp(`action_id: ${UUID_REGEX}`); + cy.getBySel('expand-event').first().click(); + cy.getBySel('securitySolutionDocumentDetailsFlyoutResponseSectionHeader').click(); + cy.getBySel('securitySolutionDocumentDetailsFlyoutResponseButton').click(); + cy.getBySel('responseActionsViewWrapper').should('exist'); + checkActionItemsInResults({ + lens: true, + discover: true, + cases: true, + timeline: true, + }); + cy.contains('View in Discover') + .should('exist') + .should('have.attr', 'href') + .then(($href) => { + // @ts-expect-error-next-line href string - check types + cy.visit($href); + cy.getBySel('breadcrumbs').contains('Discover').should('exist'); + cy.getBySel('discoverDocTable', { timeout: 60000 }).within(() => { + cy.contains(`action_data.query`); + }); + cy.contains(discoverRegex); + }); + }); + + it('can visit lens from response action results', () => { + const lensRegex = new RegExp(`Action ${UUID_REGEX} results`); + cy.getBySel('expand-event').first().click(); + cy.getBySel('securitySolutionDocumentDetailsFlyoutResponseSectionHeader').click(); + cy.getBySel('securitySolutionDocumentDetailsFlyoutResponseButton').click(); + cy.getBySel('responseActionsViewWrapper').should('exist'); + checkActionItemsInResults({ + lens: true, + discover: true, + cases: true, + timeline: true, + }); + cy.getBySel('osquery-results-comment') + .first() + .within(() => { + let lensUrl = ''; + cy.window().then((win) => { + cy.stub(win, 'open') + .as('windowOpen') + .callsFake((url) => { + lensUrl = url; + }); + }); + cy.get(`[aria-label="View in Lens"]`).click(); + cy.window() + .its('open') + .then(() => { + cy.visit(lensUrl); + }); + }); + cy.getBySel('lnsWorkspace').should('exist'); + cy.getBySel('breadcrumbs').contains(lensRegex); + }); + + it('can add to timeline from response action results', () => { + const timelineRegex = new RegExp(`Added ${UUID_REGEX} to timeline`); + const filterRegex = new RegExp(`action_id: "${UUID_REGEX}"`); + cy.getBySel('expand-event').first().click(); + cy.getBySel('securitySolutionDocumentDetailsFlyoutResponseSectionHeader').click(); + cy.getBySel('securitySolutionDocumentDetailsFlyoutResponseButton').click(); + cy.getBySel('responseActionsViewWrapper').should('exist'); + checkActionItemsInResults({ + lens: true, + discover: true, + cases: true, + timeline: true, + }); + cy.getBySel('osquery-results-comment') + .first() + .within(() => { + cy.get('.euiTableRow') + .first() + .within(() => { + cy.getBySel('add-to-timeline').click(); + }); + }); + cy.contains(timelineRegex); + cy.getBySel('securitySolutionDocumentDetailsFlyoutHeaderCollapseDetailButton').click(); + cy.getBySel('flyoutBottomBar').contains('Untitled timeline').click(); + cy.contains(filterRegex); + }); +}); diff --git a/x-pack/plugins/osquery/cypress/e2e/all/alerts_multiple_agents.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/alerts_multiple_agents.cy.ts new file mode 100644 index 0000000000000..b472a99b86568 --- /dev/null +++ b/x-pack/plugins/osquery/cypress/e2e/all/alerts_multiple_agents.cy.ts @@ -0,0 +1,106 @@ +/* + * 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 { cleanupRule, loadRule } from '../../tasks/api_fixtures'; +import { ROLE, login } from '../../tasks/login'; +import { + inputQuery, + loadRuleAlerts, + submitQuery, + takeOsqueryActionWithParams, +} from '../../tasks/live_query'; + +describe('Alert Event Details - dynamic params', () => { + let ruleId: string; + let ruleName: string; + + before(() => { + loadRule(true).then((data) => { + ruleId = data.id; + ruleName = data.name; + loadRuleAlerts(data.name); + }); + }); + + after(() => { + cleanupRule(ruleId); + }); + + beforeEach(() => { + login(ROLE.soc_manager); + cy.visit('/app/security/rules'); + cy.contains(ruleName).click(); + }); + + it('should substitute parameters in investigation guide', () => { + cy.getBySel('expand-event').first().click(); + cy.getBySel('securitySolutionDocumentDetailsFlyoutInvestigationGuideButton').click(); + cy.contains('Get processes').click(); + cy.getBySel('flyout-body-osquery').within(() => { + cy.contains("SELECT * FROM os_version where name='Ubuntu';"); + cy.contains('host.os.platform'); + cy.contains('platform'); + }); + }); + + // response-actions-notification doesn't exist in expandable flyout + it.skip('should substitute parameters in live query and increase number of ran queries', () => { + let initialNotificationCount: number; + let updatedNotificationCount: number; + cy.getBySel('expand-event').first().click(); + cy.getBySel('response-actions-notification') + .should('not.have.text', '0') + .then((element) => { + initialNotificationCount = parseInt(element.text(), 10); + }); + takeOsqueryActionWithParams(); + cy.getBySel('osquery-empty-button').click(); + cy.getBySel('response-actions-notification') + .should('not.have.text', '0') + .then((element) => { + updatedNotificationCount = parseInt(element.text(), 10); + expect(initialNotificationCount).to.be.equal(updatedNotificationCount - 1); + }) + .then(() => { + cy.getBySel('securitySolutionDocumentDetailsFlyoutResponseSectionHeader').click(); + cy.getBySel('securitySolutionDocumentDetailsFlyoutResponseButton').click(); + cy.getBySel('responseActionsViewWrapper').within(() => { + cy.contains('tags'); + cy.getBySel('osquery-results-comment').should('have.length', updatedNotificationCount); + }); + }); + + it('should be able to run take action query against all enrolled agents', () => { + cy.getBySel('expand-event').first().click(); + cy.getBySel('take-action-dropdown-btn').click(); + cy.getBySel('osquery-action-item').click(); + cy.getBySel('agentSelection').within(() => { + cy.getBySel('comboBoxClearButton').click(); + cy.getBySel('comboBoxInput').type('All{downArrow}{enter}{esc}'); + cy.contains('All agents'); + }); + inputQuery("SELECT * FROM os_version where name='{{host.os.name}}';", { + parseSpecialCharSequences: false, + }); + cy.wait(1000); + submitQuery(); + cy.getBySel('flyout-body-osquery').within(() => { + // at least 2 agents should have responded, sometimes it takes a while for the agents to respond + cy.get('[data-grid-row-index]', { timeout: 6000000 }).should('have.length.at.least', 2); + }); + }); + + it('should substitute params in osquery ran from timelines alerts', () => { + loadRuleAlerts(ruleName); + cy.getBySel('send-alert-to-timeline-button').first().click(); + cy.getBySel('query-events-table').within(() => { + cy.getBySel('expand-event').first().click(); + }); + takeOsqueryActionWithParams(); + }); + }); +}); diff --git a/x-pack/plugins/osquery/cypress/e2e/all/alerts_response_actions_form.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/alerts_response_actions_form.cy.ts new file mode 100644 index 0000000000000..38a2c5e7c1501 --- /dev/null +++ b/x-pack/plugins/osquery/cypress/e2e/all/alerts_response_actions_form.cy.ts @@ -0,0 +1,188 @@ +/* + * 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 { + cleanupPack, + cleanupRule, + loadPack, + loadRule, + multiQueryPackFixture, + packFixture, +} from '../../tasks/api_fixtures'; +import { + RESPONSE_ACTIONS_ITEM_0, + RESPONSE_ACTIONS_ITEM_1, + RESPONSE_ACTIONS_ITEM_2, + OSQUERY_RESPONSE_ACTION_ADD_BUTTON, +} from '../../tasks/response_actions'; +import { ROLE, login } from '../../tasks/login'; +import { checkActionItemsInResults, inputQuery, typeInECSFieldInput } from '../../tasks/live_query'; +import { closeDateTabIfVisible, closeToastIfVisible } from '../../tasks/integrations'; + +describe('Alert Event Details - Response Actions Form', { browser: 'electron' }, () => { + let multiQueryPackId: string; + let multiQueryPackName: string; + let ruleId: string; + let ruleName: string; + let packId: string; + let packName: string; + const packData = packFixture(); + const multiQueryPackData = multiQueryPackFixture(); + + beforeEach(() => { + loadPack(packData).then((data) => { + packId = data.saved_object_id; + packName = data.name; + }); + loadPack(multiQueryPackData).then((data) => { + multiQueryPackId = data.saved_object_id; + multiQueryPackName = data.name; + }); + loadRule().then((data) => { + ruleId = data.id; + ruleName = data.name; + }); + login(ROLE.soc_manager); + }); + afterEach(() => { + cleanupPack(packId); + cleanupPack(multiQueryPackId); + cleanupRule(ruleId); + }); + + it('adds response actions with osquery with proper validation and form values', () => { + cy.visit('/app/security/rules'); + cy.contains(ruleName).click(); + cy.getBySel('editRuleSettingsLink').click(); + cy.getBySel('globalLoadingIndicator').should('not.exist'); + closeDateTabIfVisible(); + cy.getBySel('edit-rule-actions-tab').click(); + cy.contains('Response actions are run on each rule execution.'); + cy.getBySel(OSQUERY_RESPONSE_ACTION_ADD_BUTTON).click(); + cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { + cy.contains('Query is a required field'); + inputQuery('select * from uptime1'); + }); + cy.getBySel(OSQUERY_RESPONSE_ACTION_ADD_BUTTON).click(); + cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { + cy.contains('Run a set of queries in a pack').click(); + }); + cy.contains('Save changes').click(); + cy.getBySel('response-actions-error') + .within(() => { + cy.contains('Pack is a required field'); + }) + .should('exist'); + cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { + cy.contains('Pack is a required field'); + cy.getBySel('comboBoxInput').type(`${packName}{downArrow}{enter}`); + }); + + cy.getBySel(OSQUERY_RESPONSE_ACTION_ADD_BUTTON).click(); + + cy.getBySel(RESPONSE_ACTIONS_ITEM_2).within(() => { + cy.contains('Query is a required field'); + inputQuery('select * from uptime'); + cy.contains('Advanced').click(); + typeInECSFieldInput('message{downArrow}{enter}'); + cy.getBySel('osqueryColumnValueSelect').type('days{downArrow}{enter}'); + cy.wait(1000); // wait for the validation to trigger - cypress is way faster than users ;) + }); + + cy.getBySel('ruleEditSubmitButton').click(); + cy.contains(`${ruleName} was saved`).should('exist'); + closeToastIfVisible(); + + cy.getBySel('editRuleSettingsLink').click(); + cy.getBySel('globalLoadingIndicator').should('not.exist'); + cy.getBySel('edit-rule-actions-tab').click(); + cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { + cy.contains('select * from uptime1'); + }); + cy.getBySel(RESPONSE_ACTIONS_ITEM_2).within(() => { + cy.contains('select * from uptime'); + cy.contains('Log message optimized for viewing in a log viewer'); + cy.contains('Days of uptime'); + }); + cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { + cy.contains(packName); + cy.getBySel('comboBoxInput').type('{backspace}{enter}'); + }); + cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { + cy.contains('select * from uptime1'); + cy.getBySel('remove-response-action').click(); + }); + cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { + cy.contains('Search for a pack to run'); + cy.contains('Pack is a required field'); + cy.getBySel('comboBoxInput').type(`${packName}{downArrow}{enter}`); + }); + cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { + cy.contains('select * from uptime'); + cy.contains('Log message optimized for viewing in a log viewer'); + cy.contains('Days of uptime'); + }); + cy.intercept('PUT', '/api/detection_engine/rules').as('saveRuleSingleQuery'); + cy.getBySel('ruleEditSubmitButton').click(); + cy.wait('@saveRuleSingleQuery').should(({ request }) => { + const oneQuery = [ + { + interval: 3600, + query: 'select * from uptime;', + id: Object.keys(packData.queries)[0], + }, + ]; + expect(request.body.response_actions[0].params.queries).to.deep.equal(oneQuery); + }); + + cy.contains(`${ruleName} was saved`).should('exist'); + closeToastIfVisible(); + + cy.getBySel('editRuleSettingsLink').click(); + cy.getBySel('globalLoadingIndicator').should('not.exist'); + cy.getBySel('edit-rule-actions-tab').click(); + cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { + cy.contains(packName); + cy.getBySel('comboBoxInput').type(`${multiQueryPackName}{downArrow}{enter}`); + checkActionItemsInResults({ + cases: false, + lens: false, + discover: false, + timeline: false, + }); + }); + cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { + cy.contains('select * from uptime'); + cy.contains('Log message optimized for viewing in a log viewer'); + cy.contains('Days of uptime'); + }); + cy.intercept('PUT', '/api/detection_engine/rules').as('saveRuleMultiQuery'); + + cy.contains('Save changes').click(); + cy.wait('@saveRuleMultiQuery').should(({ request }) => { + const threeQueries = [ + { + interval: 3600, + query: 'SELECT * FROM memory_info;', + platform: 'linux', + id: Object.keys(multiQueryPackData.queries)[0], + }, + { + interval: 3600, + query: 'SELECT * FROM system_info;', + id: Object.keys(multiQueryPackData.queries)[1], + }, + { + interval: 10, + query: 'select opera_extensions.* from users join opera_extensions using (uid);', + id: Object.keys(multiQueryPackData.queries)[2], + }, + ]; + expect(request.body.response_actions[0].params.queries).to.deep.equal(threeQueries); + }); + }); +}); diff --git a/x-pack/plugins/osquery/cypress/e2e/all/packs.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/packs.cy.ts deleted file mode 100644 index 6819186c5bbae..0000000000000 --- a/x-pack/plugins/osquery/cypress/e2e/all/packs.cy.ts +++ /dev/null @@ -1,934 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { recurse } from 'cypress-recurse'; -import { find } from 'lodash'; -import type { PackagePolicy } from '@kbn/fleet-plugin/common'; -import { API_VERSIONS } from '../../../common/constants'; -import { FLEET_AGENT_POLICIES, navigateTo } from '../../tasks/navigation'; -import { - checkActionItemsInResults, - checkResults, - deleteAndConfirm, - findAndClickButton, - findFormFieldByRowsLabelAndType, - inputQuery, - selectAllAgents, - submitQuery, -} from '../../tasks/live_query'; -import { ROLE, login } from '../../tasks/login'; -import { - activatePack, - cleanupAllPrebuiltPacks, - deactivatePack, - preparePack, -} from '../../tasks/packs'; -import { - addIntegration, - closeModalIfVisible, - closeToastIfVisible, - generateRandomStringName, - interceptPackId, - interceptAgentPolicyId, -} from '../../tasks/integrations'; -import { DEFAULT_POLICY, OSQUERY_POLICY } from '../../screens/fleet'; -import { - getIdFormField, - getSavedQueriesDropdown, - LIVE_QUERY_EDITOR, -} from '../../screens/live_query'; -import { - loadSavedQuery, - cleanupSavedQuery, - cleanupPack, - loadPack, - cleanupAgentPolicy, -} from '../../tasks/api_fixtures'; -import { request } from '../../tasks/common'; - -describe('ALL - Packs', () => { - let savedQueryId: string; - let savedQueryName: string; - let nomappingSavedQueryId: string; - let nomappingSavedQueryName: string; - let oneMappingSavedQueryId: string; - let oneMappingSavedQueryName: string; - let multipleMappingsSavedQueryId: string; - let multipleMappingsSavedQueryName: string; - - const integration = 'Osquery Manager'; - const PACK_NAME = 'Pack-name' + generateRandomStringName(1)[0]; - - describe('Create and edit a pack', () => { - before(() => { - loadSavedQuery().then((data) => { - savedQueryId = data.saved_object_id; - savedQueryName = data.id; - }); - loadSavedQuery({ - ecs_mapping: {}, - interval: '3600', - query: 'select * from uptime;', - }).then((data) => { - nomappingSavedQueryId = data.saved_object_id; - nomappingSavedQueryName = data.id; - }); - loadSavedQuery({ - ecs_mapping: { - 'client.geo.continent_name': { - field: 'seconds', - }, - }, - interval: '3600', - query: 'select * from uptime;', - }).then((data) => { - oneMappingSavedQueryId = data.saved_object_id; - oneMappingSavedQueryName = data.id; - }); - loadSavedQuery({ - ecs_mapping: { - labels: { - field: 'days', - }, - tags: { - field: 'seconds', - }, - 'client.address': { - field: 'total_seconds', - }, - }, - interval: '3600', - query: 'select * from uptime;', - }).then((data) => { - multipleMappingsSavedQueryId = data.saved_object_id; - multipleMappingsSavedQueryName = data.id; - }); - }); - - beforeEach(() => { - login(ROLE.soc_manager); - navigateTo('/app/osquery'); - }); - - after(() => { - cleanupSavedQuery(savedQueryId); - cleanupSavedQuery(nomappingSavedQueryId); - cleanupSavedQuery(oneMappingSavedQueryId); - cleanupSavedQuery(multipleMappingsSavedQueryId); - }); - - describe('Check if result type is correct', () => { - let resultTypePackId: string; - - before(() => { - interceptPackId((pack) => { - resultTypePackId = pack; - }); - }); - - after(() => { - cleanupPack(resultTypePackId); - }); - - it('Check if result type is correct', () => { - const packName = 'ResultType' + generateRandomStringName(1)[0]; - - cy.contains('Packs').click(); - findAndClickButton('Add pack'); - findFormFieldByRowsLabelAndType('Name', packName); - findAndClickButton('Add query'); - cy.contains('Attach next query'); - getIdFormField().type('Query1'); - inputQuery('select * from uptime;'); - cy.wait(500); // wait for the validation to trigger - cypress is way faster than users ;) - cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click(); - findAndClickButton('Add query'); - cy.contains('Attach next query'); - getIdFormField().type('Query2'); - inputQuery('select * from uptime;'); - - cy.getBySel('resultsTypeField').click(); - cy.contains('Differential').click(); - cy.wait(500); // wait for the validation to trigger - cypress is way faster than users ;) - - cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click(); - findAndClickButton('Add query'); - cy.contains('Attach next query'); - getIdFormField().type('Query3'); - inputQuery('select * from uptime;'); - cy.getBySel('resultsTypeField').click(); - cy.contains('Differential (Ignore removals)').click(); - cy.wait(500); // wait for the validation to trigger - cypress is way faster than users ;) - - cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click(); - findAndClickButton('Save pack'); - cy.getBySel('tablePaginationPopoverButton').click(); - cy.getBySel('tablePagination-50-rows').click(); - cy.react('ScheduledQueryNameComponent', { - props: { - name: packName, - }, - }).click(); - - findAndClickButton('Edit'); - cy.contains('Query1'); - cy.contains('Query2'); - cy.contains('Query3'); - cy.react('CustomItemAction', { - props: { index: 0, item: { id: 'Query1' } }, - }).click(); - cy.getBySel('resultsTypeField').contains('Snapshot').click(); - cy.contains('Differential').click(); - - cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click(); - - cy.react('CustomItemAction', { - props: { index: 0, item: { id: 'Query2' } }, - }).click(); - cy.getBySel('resultsTypeField').contains('Differential').click(); - cy.contains('Differential (Ignore removals)').click(); - - cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click(); - cy.react('CustomItemAction', { - props: { index: 0, item: { id: 'Query3' } }, - }).click(); - cy.getBySel('resultsTypeField').contains('(Ignore removals)').click(); - cy.contains('Snapshot').click(); - - cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click(); - findFormFieldByRowsLabelAndType( - 'Scheduled agent policies (optional)', - 'fleet server {downArrow} {enter}' - ); - findAndClickButton('Update pack'); - closeModalIfVisible(); - - cy.contains( - 'Create packs to organize sets of queries and to schedule queries for agent policies.' - ); - const queries = { - Query1: { - interval: 3600, - query: 'select * from uptime;', - removed: true, - snapshot: false, - }, - Query2: { - interval: 3600, - query: 'select * from uptime;', - removed: false, - snapshot: false, - }, - Query3: { - interval: 3600, - query: 'select * from uptime;', - }, - }; - request<{ items: PackagePolicy[] }>({ - url: '/internal/osquery/fleet_wrapper/package_policies', - headers: { - 'Elastic-Api-Version': API_VERSIONS.internal.v1, - }, - }).then((response) => { - const item = response.body.items.find( - (policy: PackagePolicy) => policy.policy_id === 'fleet-server-policy' - ); - - expect(item?.inputs[0].config?.osquery.value.packs[packName].queries).to.deep.equal( - queries - ); - }); - }); - }); - - describe('Check if pack is created', () => { - const packName = 'Pack-name' + generateRandomStringName(1)[0]; - let packId: string; - - before(() => { - interceptPackId((pack) => { - packId = pack; - }); - }); - - after(() => { - cleanupPack(packId); - }); - - it('should add a pack from a saved query', () => { - cy.contains('Packs').click(); - findAndClickButton('Add pack'); - findFormFieldByRowsLabelAndType('Name', packName); - findFormFieldByRowsLabelAndType('Description (optional)', 'Pack description'); - findFormFieldByRowsLabelAndType('Scheduled agent policies (optional)', DEFAULT_POLICY); - findAndClickButton('Add query'); - cy.contains('Attach next query'); - getSavedQueriesDropdown().type(`${savedQueryName}{downArrow}{enter}`); - cy.react('EuiFormRow', { props: { label: 'Interval (s)' } }) - .click() - .clear() - .type('5'); - cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click(); - cy.react('EuiTableRow').contains(savedQueryName); - findAndClickButton('Save pack'); - cy.contains('Save and deploy changes'); - findAndClickButton('Save and deploy changes'); - cy.getBySel('tablePaginationPopoverButton').click(); - cy.getBySel('tablePagination-50-rows').click(); - cy.contains(packName); - cy.contains(`Successfully created "${packName}" pack`); - closeToastIfVisible(); - }); - }); - - describe('to click the edit button and edit pack', () => { - const newQueryName = 'new-query-name' + generateRandomStringName(1)[0]; - - let packId: string; - let packName: string; - - before(() => { - loadPack({ - policy_ids: ['fleet-server-policy'], - queries: { - [savedQueryName]: { ecs_mapping: {}, interval: 3600, query: 'select * from uptime;' }, - }, - }).then((pack) => { - packId = pack.saved_object_id; - packName = pack.name; - }); - }); - - after(() => { - cleanupPack(packId); - }); - - it('', () => { - preparePack(packName); - findAndClickButton('Edit'); - cy.contains(`Edit ${packName}`); - findAndClickButton('Add query'); - cy.contains('Attach next query'); - inputQuery('select * from uptime'); - findFormFieldByRowsLabelAndType('ID', savedQueryName); - cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click(); - cy.contains('ID must be unique').should('exist'); - findFormFieldByRowsLabelAndType('ID', newQueryName); - cy.contains('ID must be unique').should('not.exist'); - cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click(); - cy.react('EuiTableRow').contains(newQueryName); - findAndClickButton('Update pack'); - cy.contains('Save and deploy changes'); - findAndClickButton('Save and deploy changes'); - cy.contains(`Successfully updated "${packName}" pack`); - closeToastIfVisible(); - }); - }); - - describe('should trigger validation when saved query is being chosen', () => { - let packId: string; - let packName: string; - - before(() => { - loadPack({ - policy_ids: ['fleet-server-policy'], - queries: { - [savedQueryName]: { ecs_mapping: {}, interval: 3600, query: 'select * from uptime;' }, - }, - }).then((pack) => { - packId = pack.saved_object_id; - packName = pack.name; - }); - }); - - after(() => { - cleanupPack(packId); - }); - - it('', () => { - preparePack(packName); - findAndClickButton('Edit'); - findAndClickButton('Add query'); - cy.contains('Attach next query'); - cy.contains('ID must be unique').should('not.exist'); - getSavedQueriesDropdown().type(`${savedQueryName}{downArrow}{enter}`); - cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click(); - cy.contains('ID must be unique').should('exist'); - cy.react('EuiFlyoutFooter').react('EuiButtonEmpty').contains('Cancel').click(); - }); - }); - - describe('should open lens in new tab', () => { - let packId: string; - let packName: string; - - before(() => { - loadPack({ - policy_ids: ['fleet-server-policy'], - queries: { - [savedQueryName]: { ecs_mapping: {}, interval: 3600, query: 'select * from uptime;' }, - }, - }).then((pack) => { - packId = pack.saved_object_id; - packName = pack.name; - }); - }); - - after(() => { - cleanupPack(packId); - }); - - it('', () => { - let lensUrl = ''; - cy.window().then((win) => { - cy.stub(win, 'open') - .as('windowOpen') - .callsFake((url) => { - lensUrl = url; - }); - }); - preparePack(packName); - cy.getBySel('docsLoading').should('exist'); - cy.getBySel('docsLoading').should('not.exist'); - cy.get(`[aria-label="View in Lens"]`).eq(0).click(); - cy.window() - .its('open') - .then(() => { - cy.visit(lensUrl); - }); - cy.getBySel('lnsWorkspace').should('exist'); - cy.getBySel('breadcrumbs').contains(`Action pack_${packName}_${savedQueryName}`); - }); - }); - - describe.skip('should open discover in new tab', () => { - let packId: string; - let packName: string; - - before(() => { - loadPack({ - policy_ids: ['fleet-server-policy'], - queries: { - [savedQueryName]: { ecs_mapping: {}, interval: 3600, query: 'select * from uptime;' }, - }, - }).then((pack) => { - packId = pack.saved_object_id; - packName = pack.name; - }); - }); - - after(() => { - cleanupPack(packId); - }); - - it('', () => { - preparePack(packName); - cy.react('CustomItemAction', { - props: { index: 0, item: { id: savedQueryName } }, - }) - .should('exist') - .within(() => { - cy.get('a') - .should('have.attr', 'href') - .then(($href) => { - // @ts-expect-error-next-line href string - check types - cy.visit($href); - cy.getBySel('breadcrumbs').contains('Discover').should('exist'); - cy.contains(`action_id: pack_${PACK_NAME}_${savedQueryName}`); - cy.getBySel('superDatePickerToggleQuickMenuButton').click(); - cy.getBySel('superDatePickerCommonlyUsed_Today').click(); - cy.getBySel('discoverDocTable', { timeout: 60000 }).contains( - `pack_${PACK_NAME}_${savedQueryName}` - ); - }); - }); - }); - }); - - describe('deactivate and activate pack', () => { - let packId: string; - let packName: string; - - before(() => { - loadPack({ - policy_ids: ['fleet-server-policy'], - queries: { - [savedQueryName]: { ecs_mapping: {}, interval: 3600, query: 'select * from uptime;' }, - }, - }).then((pack) => { - packId = pack.saved_object_id; - packName = pack.name; - }); - }); - - after(() => { - cleanupPack(packId); - }); - - it('', () => { - cy.contains('Packs').click(); - deactivatePack(packName); - activatePack(packName); - }); - }); - - describe('should verify that packs are triggered', () => { - let packId: string; - let packName: string; - - before(() => { - loadPack({ - policy_ids: ['fleet-server-policy'], - queries: { - [savedQueryName]: { ecs_mapping: {}, interval: 60, query: 'select * from uptime;' }, - }, - }).then((pack) => { - packId = pack.saved_object_id; - packName = pack.name; - }); - }); - - after(() => { - cleanupPack(packId); - }); - - it('', () => { - preparePack(packName); - cy.contains(`${packName} details`).should('exist'); - - recurse( - () => { - cy.waitForReact(); - - cy.getBySel('docsLoading').should('exist'); - cy.getBySel('docsLoading').should('not.exist'); - - return cy.get('tbody .euiTableRow > td:nth-child(5)').invoke('text'); - }, - (response) => response === 'Docs1', - { - timeout: 300000, - post: () => { - cy.reload(); - }, - } - ); - - cy.react('ScheduledQueryLastResults', { options: { timeout: 3000 } }) - .should('exist') - .within(() => { - cy.react('FormattedRelative'); - }); - - cy.react('DocsColumnResults').within(() => { - cy.react('EuiNotificationBadge').contains('1'); - }); - cy.react('AgentsColumnResults').within(() => { - cy.react('EuiNotificationBadge').contains('1'); - }); - cy.getBySel('packResultsErrorsEmpty').should('have.length', 1); - }); - }); - - describe('delete all queries in the pack', () => { - let packId: string; - let packName: string; - - before(() => { - loadPack({ - policy_ids: ['fleet-server-policy'], - queries: { - [savedQueryName]: { ecs_mapping: {}, interval: 3600, query: 'select * from uptime;' }, - }, - }).then((pack) => { - packId = pack.saved_object_id; - packName = pack.name; - }); - }); - - after(() => { - cleanupPack(packId); - }); - - it('', () => { - preparePack(packName); - cy.contains(/^Edit$/).click(); - - cy.getBySel('checkboxSelectAll').click(); - - cy.contains(/^Delete \d+ quer(y|ies)/).click(); - cy.contains(/^Update pack$/).click(); - cy.react('EuiButtonDisplay') - .contains(/^Save and deploy changes$/) - .click(); - cy.get('a').contains(packName).click(); - cy.contains(`${packName} details`).should('exist'); - cy.contains(/^No items found/).should('exist'); - }); - }); - - describe('enable changing saved queries and ecs_mappings', () => { - let packId: string; - let packName: string; - - before(() => { - loadPack({ - policy_ids: ['fleet-server-policy'], - queries: { - [savedQueryName]: { ecs_mapping: {}, interval: 3600, query: 'select * from uptime;' }, - }, - }).then((pack) => { - packId = pack.saved_object_id; - packName = pack.name; - }); - }); - - after(() => { - cleanupPack(packId); - }); - - it('', () => { - preparePack(packName); - cy.contains(/^Edit$/).click(); - - findAndClickButton('Add query'); - - getSavedQueriesDropdown().type(`${multipleMappingsSavedQueryName} {downArrow} {enter}`); - cy.contains('Custom key/value pairs').should('exist'); - cy.contains('Days of uptime').should('exist'); - cy.contains('List of keywords used to tag each').should('exist'); - cy.contains('Seconds of uptime').should('exist'); - cy.contains('Client network address.').should('exist'); - cy.contains('Total uptime seconds').should('exist'); - cy.getBySel('ECSMappingEditorForm').should('have.length', 4); - - getSavedQueriesDropdown().type(`${nomappingSavedQueryName} {downArrow} {enter}`); - cy.contains('Custom key/value pairs').should('not.exist'); - cy.contains('Days of uptime').should('not.exist'); - cy.contains('List of keywords used to tag each').should('not.exist'); - cy.contains('Seconds of uptime').should('not.exist'); - cy.contains('Client network address.').should('not.exist'); - cy.contains('Total uptime seconds').should('not.exist'); - cy.getBySel('ECSMappingEditorForm').should('have.length', 1); - - getSavedQueriesDropdown().type(`${oneMappingSavedQueryName} {downArrow} {enter}`); - cy.contains('Name of the continent').should('exist'); - cy.contains('Seconds of uptime').should('exist'); - cy.getBySel('ECSMappingEditorForm').should('have.length', 2); - - findAndClickButton('Save'); - cy.react('CustomItemAction', { - props: { index: 0, item: { id: oneMappingSavedQueryName } }, - }).click(); - cy.contains('Name of the continent').should('exist'); - cy.contains('Seconds of uptime').should('exist'); - }); - }); - - describe('to click delete button', () => { - let packName: string; - - before(() => { - loadPack({ - policy_ids: ['fleet-server-policy'], - queries: { - [savedQueryName]: { ecs_mapping: {}, interval: 3600, query: 'select * from uptime;' }, - }, - }).then((pack) => { - packName = pack.name; - }); - }); - - it('', () => { - preparePack(packName); - findAndClickButton('Edit'); - deleteAndConfirm('pack'); - }); - }); - }); - - describe('Validate that agent policy is getting removed from pack if we remove agent policy', () => { - beforeEach(() => { - login(); - }); - const AGENT_POLICY_NAME = `PackTest` + generateRandomStringName(1)[0]; - const REMOVING_PACK = 'removing-pack' + generateRandomStringName(1)[0]; - - it('add integration', () => { - cy.visit(FLEET_AGENT_POLICIES); - cy.contains('Create agent policy').click(); - cy.get('input[placeholder*="Choose a name"]').type(AGENT_POLICY_NAME); - cy.get('.euiFlyoutFooter').contains('Create agent policy').click(); - cy.contains(`Agent policy '${AGENT_POLICY_NAME}' created`); - cy.visit(FLEET_AGENT_POLICIES); - cy.contains(AGENT_POLICY_NAME).click(); - cy.contains('Add integration').click(); - cy.contains(integration).click(); - addIntegration(AGENT_POLICY_NAME); - cy.contains('Add Elastic Agent later').click(); - navigateTo('app/osquery/packs'); - findAndClickButton('Add pack'); - findFormFieldByRowsLabelAndType('Name', REMOVING_PACK); - findFormFieldByRowsLabelAndType('Scheduled agent policies (optional)', AGENT_POLICY_NAME); - findAndClickButton('Save pack'); - - closeToastIfVisible(); - cy.getBySel('tablePaginationPopoverButton').click(); - cy.getBySel('tablePagination-50-rows').click(); - cy.react('ScheduledQueryNameComponent', { props: { name: REMOVING_PACK } }).click(); - cy.contains(`${REMOVING_PACK} details`).should('exist'); - findAndClickButton('Edit'); - cy.react('EuiComboBoxInput', { props: { value: AGENT_POLICY_NAME } }).should('exist'); - - cy.visit(FLEET_AGENT_POLICIES); - cy.contains(AGENT_POLICY_NAME).click(); - cy.get('.euiTableCellContent') - .get('.euiPopover__anchor') - .get(`[aria-label="Open"]`) - .first() - .click(); - cy.contains(/^Delete integration$/).click(); - closeModalIfVisible(); - cy.contains(/^Deleted integration 'osquery_manager-*/); - navigateTo('app/osquery/packs'); - cy.contains(REMOVING_PACK).click(); - cy.contains(`${REMOVING_PACK} details`).should('exist'); - cy.wait(1000); - findAndClickButton('Edit'); - cy.react('EuiComboBoxInput', { props: { value: '' } }).should('exist'); - }); - }); - - describe('Load prebuilt packs', () => { - beforeEach(() => { - login(ROLE.soc_manager); - navigateTo('/app/osquery/packs'); - }); - - after(() => { - cleanupAllPrebuiltPacks(); - }); - - const PREBUILD_PACK_NAME = 'it-compliance'; - - it('should load prebuilt packs', () => { - cy.contains('Load Elastic prebuilt packs').click(); - cy.contains('Load Elastic prebuilt packs').should('not.exist'); - cy.wait(1000); - cy.react('EuiTableRow').should('have.length.above', 5); - }); - - it('should be able to activate pack', () => { - activatePack(PREBUILD_PACK_NAME); - deactivatePack(PREBUILD_PACK_NAME); - }); - - it('should be able to add policy to it', () => { - cy.contains(PREBUILD_PACK_NAME).click(); - cy.contains('Edit').click(); - findFormFieldByRowsLabelAndType( - 'Scheduled agent policies (optional)', - 'fleet server {downArrow}{enter}' - ); - cy.contains('Update pack').click(); - cy.getBySel('confirmModalConfirmButton').click(); - cy.contains(`Successfully updated "${PREBUILD_PACK_NAME}" pack`); - }); - - it('should be able to activate pack with agent inside', () => { - activatePack(PREBUILD_PACK_NAME); - deactivatePack(PREBUILD_PACK_NAME); - }); - it('should not be able to update prebuilt pack', () => { - cy.contains(PREBUILD_PACK_NAME).click(); - cy.contains('Edit').click(); - cy.react('EuiFieldText', { props: { name: 'name', isDisabled: true } }); - cy.react('EuiFieldText', { props: { name: 'description', isDisabled: true } }); - cy.contains('Add Query').should('not.exist'); - cy.react('ExpandedItemActions', { options: { timeout: 1000 } }); - cy.get('.euiTableRowCell--hasActions').should('not.exist'); - }); - it('should be able to delete prebuilt pack and add it again', () => { - cy.contains(PREBUILD_PACK_NAME).click(); - cy.contains('Edit').click(); - deleteAndConfirm('pack'); - cy.contains(PREBUILD_PACK_NAME).should('not.exist'); - cy.contains('Update Elastic prebuilt packs').click(); - cy.contains('Successfully updated prebuilt packs'); - cy.contains(PREBUILD_PACK_NAME).should('exist'); - }); - - it('should be able to run live prebuilt pack', () => { - navigateTo('/app/osquery/live_queries'); - cy.contains('New live query').click(); - cy.contains('Run a set of queries in a pack.').click(); - cy.get(LIVE_QUERY_EDITOR).should('not.exist'); - cy.getBySel('select-live-pack').click().type('osquery-monitoring{downArrow}{enter}'); - selectAllAgents(); - submitQuery(); - cy.getBySel('live-query-loading').should('exist'); - cy.getBySel('live-query-loading', { timeout: 10000 }).should('not.exist'); - cy.getBySel('toggleIcon-events').click(); - checkResults(); - checkActionItemsInResults({ - lens: true, - discover: true, - cases: true, - timeline: false, - }); - navigateTo('/app/osquery'); - cy.contains('osquery-monitoring'); - }); - }); - - describe('Global packs', () => { - beforeEach(() => { - login(); - navigateTo('/app/osquery/packs'); - }); - - describe('add proper shard to policies packs config', () => { - const globalPack = 'globalPack' + generateRandomStringName(1)[0]; - const agentPolicy = 'testGlobal' + generateRandomStringName(1)[0]; - let globalPackId: string; - let agentPolicyId: string; - - before(() => { - interceptPackId((pack) => { - globalPackId = pack; - }); - interceptAgentPolicyId((policyId) => { - agentPolicyId = policyId; - }); - }); - - after(() => { - cleanupPack(globalPackId); - cleanupAgentPolicy(agentPolicyId); - }); - - it('add global packs to policies', () => { - findAndClickButton('Add pack'); - findFormFieldByRowsLabelAndType('Name', globalPack); - cy.getBySel('policyIdsComboBox').should('exist'); - cy.getBySel('osqueryPackTypeGlobal').click(); - cy.getBySel('policyIdsComboBox').should('not.exist'); - - findAndClickButton('Save pack'); - - cy.getBySel('tablePaginationPopoverButton').click(); - cy.getBySel('tablePagination-50-rows').click(); - cy.contains(globalPack); - cy.contains(`Successfully created "${globalPack}" pack`); - closeToastIfVisible(); - - cy.visit(FLEET_AGENT_POLICIES); - cy.contains('Create agent policy').click(); - cy.getBySel('createAgentPolicyNameField').type(agentPolicy); - cy.getBySel('createAgentPolicyFlyoutBtn').click(); - cy.contains(`Agent policy '${agentPolicy}' created`).click(); - cy.contains(agentPolicy).click(); - cy.contains('Add integration').click(); - cy.contains(integration).click(); - addIntegration(agentPolicy); - cy.contains('Add Elastic Agent later').click(); - cy.contains('osquery_manager-'); - request<{ items: PackagePolicy[] }>({ - url: '/internal/osquery/fleet_wrapper/package_policies', - headers: { - 'Elastic-Api-Version': API_VERSIONS.internal.v1, - }, - }).then((response) => { - const item = find(response.body.items, ['policy_id', agentPolicyId]); - - expect(item?.inputs[0].config?.osquery.value.packs[globalPack]).to.deep.equal({ - shard: 100, - queries: {}, - }); - }); - cy.visit('/app/fleet/policies'); - cy.contains('td', agentPolicy) - .parent() - .within(() => { - cy.contains('rev. 2').click(); - }); - }); - }); - - describe('add proper shard to policies packs config', () => { - let shardPackId: string; - - before(() => { - interceptPackId((pack) => { - shardPackId = pack; - }); - }); - - after(() => { - cleanupPack(shardPackId); - }); - - it('', () => { - const shardPack = 'shardPack' + generateRandomStringName(1)[0]; - - findAndClickButton('Add pack'); - findFormFieldByRowsLabelAndType('Name', shardPack); - - cy.contains('Partial deployment (shards)').click(); - cy.getBySel('packShardsForm-0').within(() => { - cy.getBySel('shards-field-policy').type(`${DEFAULT_POLICY}{downArrow}{enter}`); - cy.get('#shardsPercentage0').type('{backspace}{backspace}5'); - }); - cy.getBySel('packShardsForm-1').within(() => { - cy.getBySel('shards-field-policy').type(`${OSQUERY_POLICY}{downArrow}{enter}`); - cy.get('#shardsPercentage1').type('{backspace}{backspace}{backspace}'); - }); - findAndClickButton('Save pack'); - - cy.contains(`Successfully created "${shardPack}" pack`); - closeToastIfVisible(); - - request<{ items: PackagePolicy[] }>({ - url: '/internal/osquery/fleet_wrapper/package_policies', - headers: { - 'Elastic-Api-Version': API_VERSIONS.internal.v1, - }, - }).then((response) => { - const shardPolicy = response.body.items.find( - (policy: PackagePolicy) => policy.policy_id === 'fleet-server-policy' - ); - - expect(shardPolicy?.inputs[0].config?.osquery.value.packs[shardPack]).to.deep.equal({ - shard: 15, - queries: {}, - }); - }); - cy.getBySel('tablePaginationPopoverButton').click(); - cy.getBySel('tablePagination-50-rows').click(); - cy.contains(shardPack).click(); - cy.contains('Edit').click(); - cy.get('#shardsPercentage0').should('have.value', '15'); - cy.getBySel('packShardsForm-1').within(() => { - cy.getBySel('shards-field-policy').contains(OSQUERY_POLICY); - cy.get('#shardsPercentage1').should('have.value', '0'); - }); - cy.getBySel('policyIdsComboBox').within(() => { - cy.contains(OSQUERY_POLICY).should('not.exist'); - }); - - cy.getBySel('comboBoxInput').contains(OSQUERY_POLICY).should('exist'); - cy.getBySel('policyIdsComboBox').click(); - cy.get('[data-test-subj="packShardsForm-1"]').within(() => { - cy.get(`[aria-label="Delete shards row"]`).click(); - }); - cy.getBySel('comboBoxInput').contains(OSQUERY_POLICY).should('not.exist'); - cy.getBySel('policyIdsComboBox').click(); - cy.contains(OSQUERY_POLICY).should('exist'); - }); - }); - }); -}); diff --git a/x-pack/plugins/osquery/cypress/e2e/all/packs_create_edit.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/packs_create_edit.cy.ts new file mode 100644 index 0000000000000..da32bf75ba1ae --- /dev/null +++ b/x-pack/plugins/osquery/cypress/e2e/all/packs_create_edit.cy.ts @@ -0,0 +1,628 @@ +/* + * 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 { recurse } from 'cypress-recurse'; +import type { PackagePolicy } from '@kbn/fleet-plugin/common'; +import { API_VERSIONS } from '../../../common/constants'; +import { navigateTo } from '../../tasks/navigation'; +import { + deleteAndConfirm, + findAndClickButton, + findFormFieldByRowsLabelAndType, + inputQuery, +} from '../../tasks/live_query'; +import { ROLE, login } from '../../tasks/login'; +import { activatePack, deactivatePack, preparePack } from '../../tasks/packs'; +import { + closeModalIfVisible, + closeToastIfVisible, + generateRandomStringName, + interceptPackId, +} from '../../tasks/integrations'; +import { DEFAULT_POLICY } from '../../screens/fleet'; +import { getIdFormField, getSavedQueriesDropdown } from '../../screens/live_query'; +import { loadSavedQuery, cleanupSavedQuery, cleanupPack, loadPack } from '../../tasks/api_fixtures'; +import { request } from '../../tasks/common'; + +describe('Packs - Create and Edit', () => { + let savedQueryId: string; + let savedQueryName: string; + let nomappingSavedQueryId: string; + let nomappingSavedQueryName: string; + let oneMappingSavedQueryId: string; + let oneMappingSavedQueryName: string; + let multipleMappingsSavedQueryId: string; + let multipleMappingsSavedQueryName: string; + + const PACK_NAME = 'Pack-name' + generateRandomStringName(1)[0]; + + before(() => { + loadSavedQuery().then((data) => { + savedQueryId = data.saved_object_id; + savedQueryName = data.id; + }); + loadSavedQuery({ + ecs_mapping: {}, + interval: '3600', + query: 'select * from uptime;', + }).then((data) => { + nomappingSavedQueryId = data.saved_object_id; + nomappingSavedQueryName = data.id; + }); + loadSavedQuery({ + ecs_mapping: { + 'client.geo.continent_name': { + field: 'seconds', + }, + }, + interval: '3600', + query: 'select * from uptime;', + }).then((data) => { + oneMappingSavedQueryId = data.saved_object_id; + oneMappingSavedQueryName = data.id; + }); + loadSavedQuery({ + ecs_mapping: { + labels: { + field: 'days', + }, + tags: { + field: 'seconds', + }, + 'client.address': { + field: 'total_seconds', + }, + }, + interval: '3600', + query: 'select * from uptime;', + }).then((data) => { + multipleMappingsSavedQueryId = data.saved_object_id; + multipleMappingsSavedQueryName = data.id; + }); + }); + + beforeEach(() => { + login(ROLE.soc_manager); + navigateTo('/app/osquery'); + }); + + after(() => { + cleanupSavedQuery(savedQueryId); + cleanupSavedQuery(nomappingSavedQueryId); + cleanupSavedQuery(oneMappingSavedQueryId); + cleanupSavedQuery(multipleMappingsSavedQueryId); + }); + + describe('Check if result type is correct', () => { + let resultTypePackId: string; + + before(() => { + interceptPackId((pack) => { + resultTypePackId = pack; + }); + }); + + after(() => { + cleanupPack(resultTypePackId); + }); + + it('Check if result type is correct', () => { + const packName = 'ResultType' + generateRandomStringName(1)[0]; + + cy.contains('Packs').click(); + findAndClickButton('Add pack'); + findFormFieldByRowsLabelAndType('Name', packName); + findAndClickButton('Add query'); + cy.contains('Attach next query'); + getIdFormField().type('Query1'); + inputQuery('select * from uptime;'); + cy.wait(500); // wait for the validation to trigger - cypress is way faster than users ;) + cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click(); + findAndClickButton('Add query'); + cy.contains('Attach next query'); + getIdFormField().type('Query2'); + inputQuery('select * from uptime;'); + + cy.getBySel('resultsTypeField').click(); + cy.contains('Differential').click(); + cy.wait(500); // wait for the validation to trigger - cypress is way faster than users ;) + + cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click(); + findAndClickButton('Add query'); + cy.contains('Attach next query'); + getIdFormField().type('Query3'); + inputQuery('select * from uptime;'); + cy.getBySel('resultsTypeField').click(); + cy.contains('Differential (Ignore removals)').click(); + cy.wait(500); // wait for the validation to trigger - cypress is way faster than users ;) + + cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click(); + findAndClickButton('Save pack'); + cy.getBySel('tablePaginationPopoverButton').click(); + cy.getBySel('tablePagination-50-rows').click(); + cy.react('ScheduledQueryNameComponent', { + props: { + name: packName, + }, + }).click(); + + findAndClickButton('Edit'); + cy.contains('Query1'); + cy.contains('Query2'); + cy.contains('Query3'); + cy.react('CustomItemAction', { + props: { index: 0, item: { id: 'Query1' } }, + }).click(); + cy.getBySel('resultsTypeField').contains('Snapshot').click(); + cy.contains('Differential').click(); + + cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click(); + + cy.react('CustomItemAction', { + props: { index: 0, item: { id: 'Query2' } }, + }).click(); + cy.getBySel('resultsTypeField').contains('Differential').click(); + cy.contains('Differential (Ignore removals)').click(); + + cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click(); + cy.react('CustomItemAction', { + props: { index: 0, item: { id: 'Query3' } }, + }).click(); + cy.getBySel('resultsTypeField').contains('(Ignore removals)').click(); + cy.contains('Snapshot').click(); + + cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click(); + findFormFieldByRowsLabelAndType( + 'Scheduled agent policies (optional)', + 'fleet server {downArrow} {enter}' + ); + findAndClickButton('Update pack'); + closeModalIfVisible(); + + cy.contains( + 'Create packs to organize sets of queries and to schedule queries for agent policies.' + ); + const queries = { + Query1: { + interval: 3600, + query: 'select * from uptime;', + removed: true, + snapshot: false, + }, + Query2: { + interval: 3600, + query: 'select * from uptime;', + removed: false, + snapshot: false, + }, + Query3: { + interval: 3600, + query: 'select * from uptime;', + }, + }; + request<{ items: PackagePolicy[] }>({ + url: '/internal/osquery/fleet_wrapper/package_policies', + headers: { + 'Elastic-Api-Version': API_VERSIONS.internal.v1, + }, + }).then((response) => { + const item = response.body.items.find( + (policy: PackagePolicy) => policy.policy_id === 'fleet-server-policy' + ); + + expect(item?.inputs[0].config?.osquery.value.packs[packName].queries).to.deep.equal( + queries + ); + }); + }); + }); + + describe('Check if pack is created', () => { + const packName = 'Pack-name' + generateRandomStringName(1)[0]; + let packId: string; + + before(() => { + interceptPackId((pack) => { + packId = pack; + }); + }); + + after(() => { + cleanupPack(packId); + }); + + it('should add a pack from a saved query', () => { + cy.contains('Packs').click(); + findAndClickButton('Add pack'); + findFormFieldByRowsLabelAndType('Name', packName); + findFormFieldByRowsLabelAndType('Description (optional)', 'Pack description'); + findFormFieldByRowsLabelAndType('Scheduled agent policies (optional)', DEFAULT_POLICY); + findAndClickButton('Add query'); + cy.contains('Attach next query'); + getSavedQueriesDropdown().type(`${savedQueryName}{downArrow}{enter}`); + cy.react('EuiFormRow', { props: { label: 'Interval (s)' } }) + .click() + .clear() + .type('5'); + cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click(); + cy.react('EuiTableRow').contains(savedQueryName); + findAndClickButton('Save pack'); + cy.contains('Save and deploy changes'); + findAndClickButton('Save and deploy changes'); + cy.getBySel('tablePaginationPopoverButton').click(); + cy.getBySel('tablePagination-50-rows').click(); + cy.contains(packName); + cy.contains(`Successfully created "${packName}" pack`); + closeToastIfVisible(); + }); + }); + + describe('to click the edit button and edit pack', () => { + const newQueryName = 'new-query-name' + generateRandomStringName(1)[0]; + + let packId: string; + let packName: string; + + before(() => { + loadPack({ + policy_ids: ['fleet-server-policy'], + queries: { + [savedQueryName]: { ecs_mapping: {}, interval: 3600, query: 'select * from uptime;' }, + }, + }).then((pack) => { + packId = pack.saved_object_id; + packName = pack.name; + }); + }); + + after(() => { + cleanupPack(packId); + }); + + it('', () => { + preparePack(packName); + findAndClickButton('Edit'); + cy.contains(`Edit ${packName}`); + findAndClickButton('Add query'); + cy.contains('Attach next query'); + inputQuery('select * from uptime'); + findFormFieldByRowsLabelAndType('ID', savedQueryName); + cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click(); + cy.contains('ID must be unique').should('exist'); + findFormFieldByRowsLabelAndType('ID', newQueryName); + cy.contains('ID must be unique').should('not.exist'); + cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click(); + cy.react('EuiTableRow').contains(newQueryName); + findAndClickButton('Update pack'); + cy.contains('Save and deploy changes'); + findAndClickButton('Save and deploy changes'); + cy.contains(`Successfully updated "${packName}" pack`); + closeToastIfVisible(); + }); + }); + + describe('should trigger validation when saved query is being chosen', () => { + let packId: string; + let packName: string; + + before(() => { + loadPack({ + policy_ids: ['fleet-server-policy'], + queries: { + [savedQueryName]: { ecs_mapping: {}, interval: 3600, query: 'select * from uptime;' }, + }, + }).then((pack) => { + packId = pack.saved_object_id; + packName = pack.name; + }); + }); + + after(() => { + cleanupPack(packId); + }); + + it('', () => { + preparePack(packName); + findAndClickButton('Edit'); + findAndClickButton('Add query'); + cy.contains('Attach next query'); + cy.contains('ID must be unique').should('not.exist'); + getSavedQueriesDropdown().type(`${savedQueryName}{downArrow}{enter}`); + cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click(); + cy.contains('ID must be unique').should('exist'); + cy.react('EuiFlyoutFooter').react('EuiButtonEmpty').contains('Cancel').click(); + }); + }); + + describe('should open lens in new tab', () => { + let packId: string; + let packName: string; + + before(() => { + loadPack({ + policy_ids: ['fleet-server-policy'], + queries: { + [savedQueryName]: { ecs_mapping: {}, interval: 3600, query: 'select * from uptime;' }, + }, + }).then((pack) => { + packId = pack.saved_object_id; + packName = pack.name; + }); + }); + + after(() => { + cleanupPack(packId); + }); + + it('', () => { + let lensUrl = ''; + cy.window().then((win) => { + cy.stub(win, 'open') + .as('windowOpen') + .callsFake((url) => { + lensUrl = url; + }); + }); + preparePack(packName); + cy.getBySel('docsLoading').should('exist'); + cy.getBySel('docsLoading').should('not.exist'); + cy.get(`[aria-label="View in Lens"]`).eq(0).click(); + cy.window() + .its('open') + .then(() => { + cy.visit(lensUrl); + }); + cy.getBySel('lnsWorkspace').should('exist'); + cy.getBySel('breadcrumbs').contains(`Action pack_${packName}_${savedQueryName}`); + }); + }); + + describe.skip('should open discover in new tab', () => { + let packId: string; + let packName: string; + + before(() => { + loadPack({ + policy_ids: ['fleet-server-policy'], + queries: { + [savedQueryName]: { ecs_mapping: {}, interval: 3600, query: 'select * from uptime;' }, + }, + }).then((pack) => { + packId = pack.saved_object_id; + packName = pack.name; + }); + }); + + after(() => { + cleanupPack(packId); + }); + + it('', () => { + preparePack(packName); + cy.react('CustomItemAction', { + props: { index: 0, item: { id: savedQueryName } }, + }) + .should('exist') + .within(() => { + cy.get('a') + .should('have.attr', 'href') + .then(($href) => { + // @ts-expect-error-next-line href string - check types + cy.visit($href); + cy.getBySel('breadcrumbs').contains('Discover').should('exist'); + cy.contains(`action_id: pack_${PACK_NAME}_${savedQueryName}`); + cy.getBySel('superDatePickerToggleQuickMenuButton').click(); + cy.getBySel('superDatePickerCommonlyUsed_Today').click(); + cy.getBySel('discoverDocTable', { timeout: 60000 }).contains( + `pack_${PACK_NAME}_${savedQueryName}` + ); + }); + }); + }); + }); + + describe('deactivate and activate pack', () => { + let packId: string; + let packName: string; + + before(() => { + loadPack({ + policy_ids: ['fleet-server-policy'], + queries: { + [savedQueryName]: { ecs_mapping: {}, interval: 3600, query: 'select * from uptime;' }, + }, + }).then((pack) => { + packId = pack.saved_object_id; + packName = pack.name; + }); + }); + + after(() => { + cleanupPack(packId); + }); + + it('', () => { + cy.contains('Packs').click(); + deactivatePack(packName); + activatePack(packName); + }); + }); + + describe('should verify that packs are triggered', () => { + let packId: string; + let packName: string; + + before(() => { + loadPack({ + policy_ids: ['fleet-server-policy'], + queries: { + [savedQueryName]: { ecs_mapping: {}, interval: 60, query: 'select * from uptime;' }, + }, + }).then((pack) => { + packId = pack.saved_object_id; + packName = pack.name; + }); + }); + + after(() => { + cleanupPack(packId); + }); + + it('', () => { + preparePack(packName); + cy.contains(`${packName} details`).should('exist'); + + recurse( + () => { + cy.waitForReact(); + + cy.getBySel('docsLoading').should('exist'); + cy.getBySel('docsLoading').should('not.exist'); + + return cy.get('tbody .euiTableRow > td:nth-child(5)').invoke('text'); + }, + (response) => response === 'Docs1', + { + timeout: 300000, + post: () => { + cy.reload(); + }, + } + ); + + cy.react('ScheduledQueryLastResults', { options: { timeout: 3000 } }) + .should('exist') + .within(() => { + cy.react('FormattedRelative'); + }); + + cy.react('DocsColumnResults').within(() => { + cy.react('EuiNotificationBadge').contains('1'); + }); + cy.react('AgentsColumnResults').within(() => { + cy.react('EuiNotificationBadge').contains('1'); + }); + cy.getBySel('packResultsErrorsEmpty').should('have.length', 1); + }); + }); + + describe('delete all queries in the pack', () => { + let packId: string; + let packName: string; + + before(() => { + loadPack({ + policy_ids: ['fleet-server-policy'], + queries: { + [savedQueryName]: { ecs_mapping: {}, interval: 3600, query: 'select * from uptime;' }, + }, + }).then((pack) => { + packId = pack.saved_object_id; + packName = pack.name; + }); + }); + + after(() => { + cleanupPack(packId); + }); + + it('', () => { + preparePack(packName); + cy.contains(/^Edit$/).click(); + + cy.getBySel('checkboxSelectAll').click(); + + cy.contains(/^Delete \d+ quer(y|ies)/).click(); + cy.contains(/^Update pack$/).click(); + cy.react('EuiButtonDisplay') + .contains(/^Save and deploy changes$/) + .click(); + cy.get('a').contains(packName).click(); + cy.contains(`${packName} details`).should('exist'); + cy.contains(/^No items found/).should('exist'); + }); + }); + + describe('enable changing saved queries and ecs_mappings', () => { + let packId: string; + let packName: string; + + before(() => { + loadPack({ + policy_ids: ['fleet-server-policy'], + queries: { + [savedQueryName]: { ecs_mapping: {}, interval: 3600, query: 'select * from uptime;' }, + }, + }).then((pack) => { + packId = pack.saved_object_id; + packName = pack.name; + }); + }); + + after(() => { + cleanupPack(packId); + }); + + it('', () => { + preparePack(packName); + cy.contains(/^Edit$/).click(); + + findAndClickButton('Add query'); + + getSavedQueriesDropdown().type(`${multipleMappingsSavedQueryName} {downArrow} {enter}`); + cy.contains('Custom key/value pairs').should('exist'); + cy.contains('Days of uptime').should('exist'); + cy.contains('List of keywords used to tag each').should('exist'); + cy.contains('Seconds of uptime').should('exist'); + cy.contains('Client network address.').should('exist'); + cy.contains('Total uptime seconds').should('exist'); + cy.getBySel('ECSMappingEditorForm').should('have.length', 4); + + getSavedQueriesDropdown().type(`${nomappingSavedQueryName} {downArrow} {enter}`); + cy.contains('Custom key/value pairs').should('not.exist'); + cy.contains('Days of uptime').should('not.exist'); + cy.contains('List of keywords used to tag each').should('not.exist'); + cy.contains('Seconds of uptime').should('not.exist'); + cy.contains('Client network address.').should('not.exist'); + cy.contains('Total uptime seconds').should('not.exist'); + cy.getBySel('ECSMappingEditorForm').should('have.length', 1); + + getSavedQueriesDropdown().type(`${oneMappingSavedQueryName} {downArrow} {enter}`); + cy.contains('Name of the continent').should('exist'); + cy.contains('Seconds of uptime').should('exist'); + cy.getBySel('ECSMappingEditorForm').should('have.length', 2); + + findAndClickButton('Save'); + cy.react('CustomItemAction', { + props: { index: 0, item: { id: oneMappingSavedQueryName } }, + }).click(); + cy.contains('Name of the continent').should('exist'); + cy.contains('Seconds of uptime').should('exist'); + }); + }); + + describe('to click delete button', () => { + let packName: string; + + before(() => { + loadPack({ + policy_ids: ['fleet-server-policy'], + queries: { + [savedQueryName]: { ecs_mapping: {}, interval: 3600, query: 'select * from uptime;' }, + }, + }).then((pack) => { + packName = pack.name; + }); + }); + + it('', () => { + preparePack(packName); + findAndClickButton('Edit'); + deleteAndConfirm('pack'); + }); + }); +}); diff --git a/x-pack/plugins/osquery/cypress/e2e/all/packs_integration.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/packs_integration.cy.ts new file mode 100644 index 0000000000000..c29000a79d171 --- /dev/null +++ b/x-pack/plugins/osquery/cypress/e2e/all/packs_integration.cy.ts @@ -0,0 +1,319 @@ +/* + * 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 { find } from 'lodash'; +import type { PackagePolicy } from '@kbn/fleet-plugin/common'; +import { API_VERSIONS } from '../../../common/constants'; +import { FLEET_AGENT_POLICIES, navigateTo } from '../../tasks/navigation'; +import { + checkActionItemsInResults, + checkResults, + deleteAndConfirm, + findAndClickButton, + findFormFieldByRowsLabelAndType, + selectAllAgents, + submitQuery, +} from '../../tasks/live_query'; +import { ROLE, login } from '../../tasks/login'; +import { activatePack, cleanupAllPrebuiltPacks, deactivatePack } from '../../tasks/packs'; +import { + addIntegration, + closeModalIfVisible, + closeToastIfVisible, + generateRandomStringName, + interceptPackId, + interceptAgentPolicyId, +} from '../../tasks/integrations'; +import { DEFAULT_POLICY, OSQUERY_POLICY } from '../../screens/fleet'; +import { LIVE_QUERY_EDITOR } from '../../screens/live_query'; +import { cleanupPack, cleanupAgentPolicy } from '../../tasks/api_fixtures'; +import { request } from '../../tasks/common'; + +describe('ALL - Packs', () => { + const integration = 'Osquery Manager'; + + describe('Validate that agent policy is getting removed from pack if we remove agent policy', () => { + beforeEach(() => { + login(); + }); + const AGENT_POLICY_NAME = `PackTest` + generateRandomStringName(1)[0]; + const REMOVING_PACK = 'removing-pack' + generateRandomStringName(1)[0]; + + it('add integration', () => { + cy.visit(FLEET_AGENT_POLICIES); + cy.contains('Create agent policy').click(); + cy.get('input[placeholder*="Choose a name"]').type(AGENT_POLICY_NAME); + cy.get('.euiFlyoutFooter').contains('Create agent policy').click(); + cy.contains(`Agent policy '${AGENT_POLICY_NAME}' created`); + cy.visit(FLEET_AGENT_POLICIES); + cy.contains(AGENT_POLICY_NAME).click(); + cy.contains('Add integration').click(); + cy.contains(integration).click(); + addIntegration(AGENT_POLICY_NAME); + cy.contains('Add Elastic Agent later').click(); + navigateTo('app/osquery/packs'); + findAndClickButton('Add pack'); + findFormFieldByRowsLabelAndType('Name', REMOVING_PACK); + findFormFieldByRowsLabelAndType('Scheduled agent policies (optional)', AGENT_POLICY_NAME); + findAndClickButton('Save pack'); + + closeToastIfVisible(); + cy.getBySel('tablePaginationPopoverButton').click(); + cy.getBySel('tablePagination-50-rows').click(); + cy.react('ScheduledQueryNameComponent', { props: { name: REMOVING_PACK } }).click(); + cy.contains(`${REMOVING_PACK} details`).should('exist'); + findAndClickButton('Edit'); + cy.react('EuiComboBoxInput', { props: { value: AGENT_POLICY_NAME } }).should('exist'); + + cy.visit(FLEET_AGENT_POLICIES); + cy.contains(AGENT_POLICY_NAME).click(); + cy.get('.euiTableCellContent') + .get('.euiPopover__anchor') + .get(`[aria-label="Open"]`) + .first() + .click(); + cy.contains(/^Delete integration$/).click(); + closeModalIfVisible(); + cy.contains(/^Deleted integration 'osquery_manager-*/); + navigateTo('app/osquery/packs'); + cy.contains(REMOVING_PACK).click(); + cy.contains(`${REMOVING_PACK} details`).should('exist'); + cy.wait(1000); + findAndClickButton('Edit'); + cy.react('EuiComboBoxInput', { props: { value: '' } }).should('exist'); + }); + }); + + describe('Load prebuilt packs', () => { + beforeEach(() => { + login(ROLE.soc_manager); + navigateTo('/app/osquery/packs'); + }); + + after(() => { + cleanupAllPrebuiltPacks(); + }); + + const PREBUILD_PACK_NAME = 'it-compliance'; + + it('should load prebuilt packs', () => { + cy.contains('Load Elastic prebuilt packs').click(); + cy.contains('Load Elastic prebuilt packs').should('not.exist'); + cy.wait(1000); + cy.react('EuiTableRow').should('have.length.above', 5); + }); + + it('should be able to activate pack', () => { + activatePack(PREBUILD_PACK_NAME); + deactivatePack(PREBUILD_PACK_NAME); + }); + + it('should be able to add policy to it', () => { + cy.contains(PREBUILD_PACK_NAME).click(); + cy.contains('Edit').click(); + findFormFieldByRowsLabelAndType( + 'Scheduled agent policies (optional)', + 'fleet server {downArrow}{enter}' + ); + cy.contains('Update pack').click(); + cy.getBySel('confirmModalConfirmButton').click(); + cy.contains(`Successfully updated "${PREBUILD_PACK_NAME}" pack`); + }); + + it('should be able to activate pack with agent inside', () => { + activatePack(PREBUILD_PACK_NAME); + deactivatePack(PREBUILD_PACK_NAME); + }); + it('should not be able to update prebuilt pack', () => { + cy.contains(PREBUILD_PACK_NAME).click(); + cy.contains('Edit').click(); + cy.react('EuiFieldText', { props: { name: 'name', isDisabled: true } }); + cy.react('EuiFieldText', { props: { name: 'description', isDisabled: true } }); + cy.contains('Add Query').should('not.exist'); + cy.react('ExpandedItemActions', { options: { timeout: 1000 } }); + cy.get('.euiTableRowCell--hasActions').should('not.exist'); + }); + it('should be able to delete prebuilt pack and add it again', () => { + cy.contains(PREBUILD_PACK_NAME).click(); + cy.contains('Edit').click(); + deleteAndConfirm('pack'); + cy.contains(PREBUILD_PACK_NAME).should('not.exist'); + cy.contains('Update Elastic prebuilt packs').click(); + cy.contains('Successfully updated prebuilt packs'); + cy.contains(PREBUILD_PACK_NAME).should('exist'); + }); + + it('should be able to run live prebuilt pack', () => { + navigateTo('/app/osquery/live_queries'); + cy.contains('New live query').click(); + cy.contains('Run a set of queries in a pack.').click(); + cy.get(LIVE_QUERY_EDITOR).should('not.exist'); + cy.getBySel('select-live-pack').click().type('osquery-monitoring{downArrow}{enter}'); + selectAllAgents(); + submitQuery(); + cy.getBySel('live-query-loading').should('exist'); + cy.getBySel('live-query-loading', { timeout: 10000 }).should('not.exist'); + cy.getBySel('toggleIcon-events').click(); + checkResults(); + checkActionItemsInResults({ + lens: true, + discover: true, + cases: true, + timeline: false, + }); + navigateTo('/app/osquery'); + cy.contains('osquery-monitoring'); + }); + }); + + describe('Global packs', () => { + beforeEach(() => { + login(); + navigateTo('/app/osquery/packs'); + }); + + describe('add proper shard to policies packs config', () => { + const globalPack = 'globalPack' + generateRandomStringName(1)[0]; + const agentPolicy = 'testGlobal' + generateRandomStringName(1)[0]; + let globalPackId: string; + let agentPolicyId: string; + + before(() => { + interceptPackId((pack) => { + globalPackId = pack; + }); + interceptAgentPolicyId((policyId) => { + agentPolicyId = policyId; + }); + }); + + after(() => { + cleanupPack(globalPackId); + cleanupAgentPolicy(agentPolicyId); + }); + + it('add global packs to policies', () => { + findAndClickButton('Add pack'); + findFormFieldByRowsLabelAndType('Name', globalPack); + cy.getBySel('policyIdsComboBox').should('exist'); + cy.getBySel('osqueryPackTypeGlobal').click(); + cy.getBySel('policyIdsComboBox').should('not.exist'); + + findAndClickButton('Save pack'); + + cy.getBySel('tablePaginationPopoverButton').click(); + cy.getBySel('tablePagination-50-rows').click(); + cy.contains(globalPack); + cy.contains(`Successfully created "${globalPack}" pack`); + closeToastIfVisible(); + + cy.visit(FLEET_AGENT_POLICIES); + cy.contains('Create agent policy').click(); + cy.getBySel('createAgentPolicyNameField').type(agentPolicy); + cy.getBySel('createAgentPolicyFlyoutBtn').click(); + cy.contains(`Agent policy '${agentPolicy}' created`).click(); + cy.contains(agentPolicy).click(); + cy.contains('Add integration').click(); + cy.contains(integration).click(); + addIntegration(agentPolicy); + cy.contains('Add Elastic Agent later').click(); + cy.contains('osquery_manager-'); + request<{ items: PackagePolicy[] }>({ + url: '/internal/osquery/fleet_wrapper/package_policies', + headers: { + 'Elastic-Api-Version': API_VERSIONS.internal.v1, + }, + }).then((response) => { + const item = find(response.body.items, ['policy_id', agentPolicyId]); + + expect(item?.inputs[0].config?.osquery.value.packs[globalPack]).to.deep.equal({ + shard: 100, + queries: {}, + }); + }); + cy.visit('/app/fleet/policies'); + cy.contains('td', agentPolicy) + .parent() + .within(() => { + cy.contains('rev. 2').click(); + }); + }); + }); + + describe('add proper shard to policies packs config', () => { + let shardPackId: string; + + before(() => { + interceptPackId((pack) => { + shardPackId = pack; + }); + }); + + after(() => { + cleanupPack(shardPackId); + }); + + it('', () => { + const shardPack = 'shardPack' + generateRandomStringName(1)[0]; + + findAndClickButton('Add pack'); + findFormFieldByRowsLabelAndType('Name', shardPack); + + cy.contains('Partial deployment (shards)').click(); + cy.getBySel('packShardsForm-0').within(() => { + cy.getBySel('shards-field-policy').type(`${DEFAULT_POLICY}{downArrow}{enter}`); + cy.get('#shardsPercentage0').type('{backspace}{backspace}5'); + }); + cy.getBySel('packShardsForm-1').within(() => { + cy.getBySel('shards-field-policy').type(`${OSQUERY_POLICY}{downArrow}{enter}`); + cy.get('#shardsPercentage1').type('{backspace}{backspace}{backspace}'); + }); + findAndClickButton('Save pack'); + + cy.contains(`Successfully created "${shardPack}" pack`); + closeToastIfVisible(); + + request<{ items: PackagePolicy[] }>({ + url: '/internal/osquery/fleet_wrapper/package_policies', + headers: { + 'Elastic-Api-Version': API_VERSIONS.internal.v1, + }, + }).then((response) => { + const shardPolicy = response.body.items.find( + (policy: PackagePolicy) => policy.policy_id === 'fleet-server-policy' + ); + + expect(shardPolicy?.inputs[0].config?.osquery.value.packs[shardPack]).to.deep.equal({ + shard: 15, + queries: {}, + }); + }); + cy.getBySel('tablePaginationPopoverButton').click(); + cy.getBySel('tablePagination-50-rows').click(); + cy.contains(shardPack).click(); + cy.contains('Edit').click(); + cy.get('#shardsPercentage0').should('have.value', '15'); + cy.getBySel('packShardsForm-1').within(() => { + cy.getBySel('shards-field-policy').contains(OSQUERY_POLICY); + cy.get('#shardsPercentage1').should('have.value', '0'); + }); + cy.getBySel('policyIdsComboBox').within(() => { + cy.contains(OSQUERY_POLICY).should('not.exist'); + }); + + cy.getBySel('comboBoxInput').contains(OSQUERY_POLICY).should('exist'); + cy.getBySel('policyIdsComboBox').click(); + cy.get('[data-test-subj="packShardsForm-1"]').within(() => { + cy.get(`[aria-label="Delete shards row"]`).click(); + }); + cy.getBySel('comboBoxInput').contains(OSQUERY_POLICY).should('not.exist'); + cy.getBySel('policyIdsComboBox').click(); + cy.contains(OSQUERY_POLICY).should('exist'); + }); + }); + }); +}); diff --git a/x-pack/plugins/osquery/cypress/e2e/roles/alert_test.cy.ts b/x-pack/plugins/osquery/cypress/e2e/roles/alert_test.cy.ts index 9aed481152bd8..69ae9de00611d 100644 --- a/x-pack/plugins/osquery/cypress/e2e/roles/alert_test.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/roles/alert_test.cy.ts @@ -97,6 +97,7 @@ describe('Alert Test', () => { cy.getBySel('expand-event').first().click(); cy.wait(500); + cy.getBySel('securitySolutionDocumentDetailsFlyoutInvestigationGuideButton').click(); cy.contains('Get processes').click(); }); diff --git a/x-pack/plugins/osquery/cypress/e2e/roles/none.cy.ts b/x-pack/plugins/osquery/cypress/e2e/roles/none.cy.ts index e35b99719bd2a..171729114dd31 100644 --- a/x-pack/plugins/osquery/cypress/e2e/roles/none.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/roles/none.cy.ts @@ -65,7 +65,8 @@ describe('None', () => { cy.visit(`/app/security/rules/id/${ruleId}/alerts`); cy.getBySel('expand-event').first().click(); cy.getBySel('take-action-dropdown-btn').click(); - cy.getBySel('responseActionsViewTab').click(); + cy.getBySel('securitySolutionDocumentDetailsFlyoutResponseSectionHeader').click(); + cy.getBySel('securitySolutionDocumentDetailsFlyoutResponseButton').click(); cy.contains('Permission denied').should('exist'); }); }); diff --git a/x-pack/plugins/osquery/cypress/support/e2e.ts b/x-pack/plugins/osquery/cypress/support/e2e.ts index 975c0a7d65efc..e848ce3ef9b8e 100644 --- a/x-pack/plugins/osquery/cypress/support/e2e.ts +++ b/x-pack/plugins/osquery/cypress/support/e2e.ts @@ -26,7 +26,9 @@ export {}; import 'cypress-react-selector'; -// import './coverage'; +import registerCypressGrep from '@cypress/grep'; + +registerCypressGrep(); declare global { // eslint-disable-next-line @typescript-eslint/no-namespace diff --git a/x-pack/plugins/osquery/cypress/tasks/api_fixtures.ts b/x-pack/plugins/osquery/cypress/tasks/api_fixtures.ts index b125693e8b915..172f7a1cbedcc 100644 --- a/x-pack/plugins/osquery/cypress/tasks/api_fixtures.ts +++ b/x-pack/plugins/osquery/cypress/tasks/api_fixtures.ts @@ -229,6 +229,7 @@ export const cleanupCase = (id: string) => { method: 'DELETE', url: '/api/cases', qs: { ids: JSON.stringify([id]) }, + failOnStatusCode: false, }); }; diff --git a/x-pack/plugins/osquery/cypress/tasks/live_query.ts b/x-pack/plugins/osquery/cypress/tasks/live_query.ts index 8c0ed1264005a..144bf8c94f6be 100644 --- a/x-pack/plugins/osquery/cypress/tasks/live_query.ts +++ b/x-pack/plugins/osquery/cypress/tasks/live_query.ts @@ -6,6 +6,7 @@ */ import { LIVE_QUERY_EDITOR } from '../screens/live_query'; +import { ROLE, login } from './login'; export const DEFAULT_QUERY = 'select * from processes;'; export const BIG_QUERY = 'select * from processes, users limit 110;'; @@ -100,6 +101,7 @@ export const toggleRuleOffAndOn = (ruleName: string) => { }; export const loadRuleAlerts = (ruleName: string) => { + login(ROLE.soc_manager); cy.visit('/app/security/rules'); cy.contains(ruleName).click(); cy.getBySel('alertsTable').within(() => { diff --git a/x-pack/plugins/osquery/package.json b/x-pack/plugins/osquery/package.json index 91b9cf67d361c..e36c3c4e7bfcc 100644 --- a/x-pack/plugins/osquery/package.json +++ b/x-pack/plugins/osquery/package.json @@ -5,8 +5,12 @@ "private": true, "license": "Elastic License 2.0", "scripts": { + "cypress:burn": "yarn cypress:run --env burn=2 --concurrency=1 --headed", + "cypress:changed-specs-only": "yarn cypress:run --changed-specs-only --env burn=2", "cypress:open": "node ../security_solution/scripts/start_cypress_parallel open --config-file ../osquery/cypress.config.ts --ftr-config-file ../../../x-pack/test/osquery_cypress/cli_config", "cypress:run": "node ../security_solution/scripts/start_cypress_parallel run --config-file ../osquery/cypress.config.ts --ftr-config-file ../../../x-pack/test/osquery_cypress/cli_config --concurrency 1", - "nyc": "../../../node_modules/.bin/nyc report --reporter=text-summary" + "nyc": "../../../node_modules/.bin/nyc report --reporter=text-summary", + "junit:merge": "../../../node_modules/.bin/mochawesome-merge ../../../target/kibana-osquery/cypress/results/mochawesome*.json > ../../../target/kibana-osquery/cypress/results/output.json && ../../../node_modules/.bin/marge ../../../target/kibana-osquery/cypress/results/output.json --reportDir ../../../target/kibana-osquery/cypress/results && yarn junit:transform && mkdir -p ../../../target/junit && cp ../../../target/kibana-osquery/cypress/results/*.xml ../../../target/junit/", + "junit:transform": "node ../security_solution/scripts/junit_transformer --pathPattern '../../../target/kibana-osquery/cypress/results/*.xml' --rootDirectory ../../../ --reportName 'Osquery Cypress' --writeInPlace" } } diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts index d81fa0c294706..0eca2cbf03501 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { pick } from 'lodash'; +import { map, pick } from 'lodash'; import type { Client, estypes } from '@elastic/elasticsearch'; import type { Agent, @@ -35,6 +35,7 @@ import type { } from '@kbn/fleet-plugin/common/types'; import nodeFetch from 'node-fetch'; import semver from 'semver'; +import axios from 'axios'; import { catchAxiosErrorFormatAndThrow } from './format_axios_error'; import { FleetAgentGenerator } from '../../../common/endpoint/data_generators/fleet_agent_generator'; @@ -236,7 +237,13 @@ export const getAgentVersionMatchingCurrentStack = async ( kbnClient: KbnClient ): Promise => { const kbnStatus = await kbnClient.status.get(); - let version = kbnStatus.version.number; + const agentVersions = await axios + .get('https://artifacts-api.elastic.co/v1/versions') + .then((response) => map(response.data.versions, (version) => version.split('-SNAPSHOT')[0])); + + let version = + semver.maxSatisfying(agentVersions, `<=${kbnStatus.version.number}`) ?? + kbnStatus.version.number; // Add `-SNAPSHOT` if version indicates it was from a snapshot or the build hash starts // with `xxxxxxxxx` (value that seems to be present when running kibana from source) diff --git a/x-pack/plugins/security_solution/scripts/run_cypress/parallel.ts b/x-pack/plugins/security_solution/scripts/run_cypress/parallel.ts index 7ea6be3314be8..19f409fb6a567 100644 --- a/x-pack/plugins/security_solution/scripts/run_cypress/parallel.ts +++ b/x-pack/plugins/security_solution/scripts/run_cypress/parallel.ts @@ -105,6 +105,10 @@ export const cli = () => { // eslint-disable-next-line no-process-exit return process.exit(0); } + + // to avoid running too many tests, we limit the number of files to 3 + // we may extend this in the future + files = files.slice(0, 3); } if (!files?.length) {