From 3cf200254e11fda188bc6086aa36d1c98cd04df3 Mon Sep 17 00:00:00 2001 From: Jorge Sanz Date: Mon, 28 Nov 2022 17:36:20 +0100 Subject: [PATCH 01/11] [DOCS] [Maps] Fix the list of EMS supported prococols (#146313) ## Summary Removes `1.3` from the list of TLS supported protocols for Elastic Maps Service server. --- docs/maps/connect-to-ems.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/maps/connect-to-ems.asciidoc b/docs/maps/connect-to-ems.asciidoc index 6ea879e64d14d..f3e0709f7f506 100644 --- a/docs/maps/connect-to-ems.asciidoc +++ b/docs/maps/connect-to-ems.asciidoc @@ -130,7 +130,7 @@ endif::[] | `ssl.supportedProtocols` | An array of supported protocols with versions. -Valid protocols: `TLSv1`, `TLSv1.1`, `TLSv1.2`, `TLSv1.3`. *Default: `TLSv1.1`, `TLSv1.2`, `TLSv1.3`*. <>. +Valid protocols: `TLSv1`, `TLSv1.1`, `TLSv1.2`. *Default: `TLSv1.1`, `TLSv1.2`*. <>. | `ssl.cipherSuites` | Details on the format, and the valid options, are available via the From 97c08ccbb0b98acc53e0012ee0d64559da8b81ec Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Mon, 28 Nov 2022 11:46:00 -0500 Subject: [PATCH 02/11] [Fleet] Fix fleetauthz tags serialization multi level (#146413) --- .../fleet/server/routes/security.test.ts | 78 ++++++++++++++++++- .../plugins/fleet/server/routes/security.ts | 11 ++- 2 files changed, 84 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/fleet/server/routes/security.test.ts b/x-pack/plugins/fleet/server/routes/security.test.ts index fac6252cc1e16..15e46529c8697 100644 --- a/x-pack/plugins/fleet/server/routes/security.test.ts +++ b/x-pack/plugins/fleet/server/routes/security.test.ts @@ -15,7 +15,7 @@ import { createAppContextStartContractMock } from '../mocks'; import { appContextService } from '../services'; import type { FleetRequestHandlerContext } from '../types'; -import { makeRouterWithFleetAuthz } from './security'; +import { deserializeAuthzConfig, makeRouterWithFleetAuthz, serializeAuthzConfig } from './security'; function getCheckPrivilegesMockedImplementation(kibanaRoles: string[]) { return (checkPrivileges: CheckPrivilegesPayload) => { @@ -198,3 +198,79 @@ describe('FleetAuthzRouter', () => { }); }); }); + +describe('serializeAuthzConfig', () => { + it('should serialize authz to tags', () => { + const res = serializeAuthzConfig({ + fleetAuthz: { + fleet: { + readEnrollmentTokens: true, + setup: true, + }, + integrations: { + readPackageInfo: true, + removePackages: true, + }, + packagePrivileges: { + endpoint: { + actions: { + readPolicyManagement: { + executePackageAction: true, + }, + readBlocklist: { + executePackageAction: true, + }, + }, + }, + }, + }, + }); + + expect(res).toEqual([ + 'fleet:authz:fleet:readEnrollmentTokens', + 'fleet:authz:fleet:setup', + 'fleet:authz:integrations:readPackageInfo', + 'fleet:authz:integrations:removePackages', + 'fleet:authz:packagePrivileges:endpoint:actions:readPolicyManagement:executePackageAction', + 'fleet:authz:packagePrivileges:endpoint:actions:readBlocklist:executePackageAction', + ]); + }); +}); + +describe('deserializeAuthzConfig', () => { + it('should deserialize tags to fleet authz', () => { + const res = deserializeAuthzConfig([ + 'fleet:authz:fleet:readEnrollmentTokens', + 'fleet:authz:fleet:setup', + 'fleet:authz:integrations:readPackageInfo', + 'fleet:authz:integrations:removePackages', + 'fleet:authz:packagePrivileges:endpoint:actions:readPolicyManagement:executePackageAction', + 'fleet:authz:packagePrivileges:endpoint:actions:readBlocklist:executePackageAction', + ]); + + expect(res).toEqual({ + fleetAuthz: { + fleet: { + readEnrollmentTokens: true, + setup: true, + }, + integrations: { + readPackageInfo: true, + removePackages: true, + }, + packagePrivileges: { + endpoint: { + actions: { + readPolicyManagement: { + executePackageAction: true, + }, + readBlocklist: { + executePackageAction: true, + }, + }, + }, + }, + }, + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/routes/security.ts b/x-pack/plugins/fleet/server/routes/security.ts index da90480944a46..8914be58294ba 100644 --- a/x-pack/plugins/fleet/server/routes/security.ts +++ b/x-pack/plugins/fleet/server/routes/security.ts @@ -184,7 +184,8 @@ function shouldHandlePostAuthRequest(req: KibanaRequest) { } return false; } -function deserializeAuthzConfig(tags: readonly string[]): FleetAuthzRouteConfig { +// Exported for test only +export function deserializeAuthzConfig(tags: readonly string[]): FleetAuthzRouteConfig { let fleetAuthz: FleetAuthzRequirements | undefined; for (const tag of tags) { if (!tag.match(/^fleet:authz/)) { @@ -199,7 +200,7 @@ function deserializeAuthzConfig(tags: readonly string[]): FleetAuthzRouteConfig .replace(/^fleet:authz:/, '') .split(':') .reduce((acc: any, key, idx, keys) => { - if (idx === keys.length + 1) { + if (idx === keys.length - 1) { acc[key] = true; return acc; @@ -215,7 +216,9 @@ function deserializeAuthzConfig(tags: readonly string[]): FleetAuthzRouteConfig return { fleetAuthz }; } -function serializeAuthzConfig(config: FleetAuthzRouteConfig): string[] { + +// Exported for test only +export function serializeAuthzConfig(config: FleetAuthzRouteConfig): string[] { const tags: string[] = []; if (config.fleetAuthz) { @@ -224,7 +227,7 @@ function serializeAuthzConfig(config: FleetAuthzRouteConfig): string[] { if (typeof requirements[key] === 'boolean') { tags.push(`fleet:authz:${prefix}${key}`); } else if (typeof requirements[key] !== 'undefined') { - fleetAuthzToTags(requirements[key] as DeepPartialTruthy, `${key}:`); + fleetAuthzToTags(requirements[key] as DeepPartialTruthy, `${prefix}${key}:`); } } } From 3c30b3f8ce58da3316959a3bee36d89d6fcc2103 Mon Sep 17 00:00:00 2001 From: Ievgen Sorokopud Date: Mon, 28 Nov 2022 17:47:29 +0100 Subject: [PATCH 03/11] [Security Solution] Stop Job should show when ml job toggle is on (#146407) ## Summary This PR fixes incorrect wording of the ML Job run/stop status label. Right now we always show "Run job" label even when job has been started already. With this fix we will show "Stop job" in case of running job. Job is not running state: Screenshot 2022-11-28 at 15 55 37 Job is running state: Screenshot 2022-11-28 at 15 55 29 Ticket: #145324 --- .../rules/description_step/ml_job_description.tsx | 7 +++---- .../components/rules/description_step/translations.tsx | 7 +++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/ml_job_description.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/ml_job_description.tsx index 780998d904c8f..abe5318b92c13 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/ml_job_description.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/ml_job_description.tsx @@ -93,6 +93,7 @@ const MlJobDescriptionComponent: React.FC<{ }); const jobIdSpan = {jobId}; + const isStarted = isJobStarted(job.jobState, job.datafeedState); const handleJobStateChange = useCallback( async (_, latestTimestampMs: number, enable: boolean) => { @@ -102,7 +103,7 @@ const MlJobDescriptionComponent: React.FC<{ [enableDatafeed, job, refreshJob] ); - return job != null ? ( + return (
@@ -122,12 +123,10 @@ const MlJobDescriptionComponent: React.FC<{ /> - {i18n.ML_RUN_JOB_LABEL} + {isStarted ? i18n.ML_STOP_JOB_LABEL : i18n.ML_RUN_JOB_LABEL} - ) : ( - jobIdSpan ); }; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/translations.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/translations.tsx index ed7f761bec143..c7a3de2fbbcf7 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/translations.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/translations.tsx @@ -98,6 +98,13 @@ export const ML_RUN_JOB_LABEL = i18n.translate( } ); +export const ML_STOP_JOB_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.mlStopJobLabel', + { + defaultMessage: 'Stop job', + } +); + export const ML_JOB_STARTED = i18n.translate( 'xpack.securitySolution.detectionEngine.ruleDescription.mlJobStartedDescription', { From 48adb0c467dff93d3cbb145c62ec5acda9474619 Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Mon, 28 Nov 2022 11:49:31 -0500 Subject: [PATCH 04/11] [Security Solution][Endpoint] Change Policy list page empty state onboarding screen to support RBAC (#146180) ## Summary - Changes the Policy List onboarding screen so that if the user does NOT have access to Fleet, then the "Add Elastic Defend" button is hidden and a message is displayed instead that points the user to the documentation --- .../components/management_empty_state.tsx | 207 ++++++++++-------- .../pages/endpoint_hosts/view/index.tsx | 24 +- 2 files changed, 114 insertions(+), 117 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/components/management_empty_state.tsx b/x-pack/plugins/security_solution/public/management/components/management_empty_state.tsx index d03c6d9007236..113dc8985a403 100644 --- a/x-pack/plugins/security_solution/public/management/components/management_empty_state.tsx +++ b/x-pack/plugins/security_solution/public/management/components/management_empty_state.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import type { MouseEvent, CSSProperties, ReactNode } from 'react'; +import type { MouseEvent, CSSProperties } from 'react'; import React, { useMemo } from 'react'; import type { EuiSelectableProps } from '@elastic/eui'; import { @@ -21,9 +21,11 @@ import { EuiIcon, EuiLoadingSpinner, EuiLink, + EuiLoadingContent, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { useUserPrivileges } from '../../common/components/user_privileges'; import onboardingLogo from '../images/security_administration_onboarding.svg'; import { useKibana } from '../../common/lib/kibana'; @@ -41,109 +43,126 @@ interface ManagementStep { children: JSX.Element; } +const MissingFleetAccessInfo = React.memo(() => { + const { services } = useKibana(); + + return ( + + + + + + + ); +}); +MissingFleetAccessInfo.displayName = 'MissingFleetAccessInfo'; + const PolicyEmptyState = React.memo<{ loading: boolean; onActionClick?: (event: MouseEvent) => void; actionDisabled?: boolean; - actionHidden?: boolean; - additionalInfo?: ReactNode; policyEntryPoint?: boolean; -}>( - ({ - loading, - onActionClick, - actionDisabled, - actionHidden, - additionalInfo, - policyEntryPoint = false, - }) => { - const docLinks = useKibana().services.docLinks; - return ( -
- {loading ? ( - - - - - - ) : ( - - - -

- -

-
- - +}>(({ loading, onActionClick, actionDisabled, policyEntryPoint = false }) => { + const docLinks = useKibana().services.docLinks; + const { canAccessFleet, loading: authzLoading } = useUserPrivileges().endpointPrivileges; + + return ( +
+ {loading ? ( + + + + + + ) : ( + + + +

- - - - {policyEntryPoint ? ( - - ) : ( - - )} - - - +

+
+ + + + + + + {policyEntryPoint ? ( + ) : ( + - - - - - - {additionalInfo} - {!actionHidden && ( - <> - - - - - - - - - )} -
- - - -
- )} -
- ); - } -); +
+ + + + + + + + + + + {authzLoading && } + + {!authzLoading && canAccessFleet && ( + <> + + + + + + + + + + )} + + {!authzLoading && !canAccessFleet && } +
+ + + + +
+ )} +
+ ); +}); const EndpointsEmptyState = React.memo<{ loading: boolean; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx index 0986d8acaf013..ce53039039b3f 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx @@ -178,23 +178,6 @@ export const EndpointList = () => { ); - const missingFleetAccessInfo = useMemo(() => { - return ( - - - - - - - ); - }, [services.docLinks.links.securitySolution.privileges]); - useEffect(() => { // if no endpoint policy, skip transform check if (!shouldCheckTransforms || !policyItems || !policyItems.length) { @@ -572,11 +555,7 @@ export const EndpointList = () => { } else if (canReadEndpointList && !canAccessFleet) { return ( - + ); } else if (!policyItemsLoading && policyItems && policyItems.length > 0) { @@ -616,7 +595,6 @@ export const EndpointList = () => { canAccessFleet, canReadEndpointList, endpointPrivilegesLoading, - missingFleetAccessInfo, ]); const hasListData = listData && listData.length > 0; From 3a6825cf47e80dab9aeae373a1a93ab740e733eb Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Mon, 28 Nov 2022 09:52:03 -0700 Subject: [PATCH 05/11] Fixes auto suggest in security solution (#145924) --- .../query_string_input/query_string_input.tsx | 8 ++------ .../cypress/e2e/header/search_bar.cy.ts | 20 +++++++++++++++++-- .../cypress/screens/search_bar.ts | 7 +++++++ .../cypress/tasks/search_bar.ts | 11 ++++++++++ 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/plugins/unified_search/public/query_string_input/query_string_input.tsx b/src/plugins/unified_search/public/query_string_input/query_string_input.tsx index cc4e934b1270e..4f6737cec44a0 100644 --- a/src/plugins/unified_search/public/query_string_input/query_string_input.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_string_input.tsx @@ -31,11 +31,7 @@ import { compact, debounce, isEmpty, isEqual, isFunction, partition } from 'loda import { CoreStart, DocLinksStart, Toast } from '@kbn/core/public'; import type { Query } from '@kbn/es-query'; import { DataPublicPluginStart, getQueryLog } from '@kbn/data-plugin/public'; -import { - type DataView, - DataView as KibanaDataView, - DataViewsPublicPluginStart, -} from '@kbn/data-views-plugin/public'; +import { type DataView, DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { PersistedLog } from '@kbn/data-plugin/public'; import { getFieldSubtypeNested, KIBANA_USER_QUERY_LANGUAGE_KEY } from '@kbn/data-plugin/common'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; @@ -188,7 +184,7 @@ export default class QueryStringInputUI extends PureComponent(this.props.indexPatterns || [], (indexPattern): indexPattern is DataView => { - return indexPattern instanceof KibanaDataView; + return indexPattern.hasOwnProperty('fields') && indexPattern.hasOwnProperty('title'); }); const idOrTitlePatterns = stringPatterns.map((sp) => typeof sp === 'string' ? { type: 'title', value: sp } : sp diff --git a/x-pack/plugins/security_solution/cypress/e2e/header/search_bar.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/header/search_bar.cy.ts index 8e7bd5f38b068..e09dac46c04e7 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/header/search_bar.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/header/search_bar.cy.ts @@ -6,8 +6,17 @@ */ import { login, visit } from '../../tasks/login'; -import { openAddFilterPopover, fillAddFilterForm } from '../../tasks/search_bar'; -import { GLOBAL_SEARCH_BAR_FILTER_ITEM } from '../../screens/search_bar'; +import { + openAddFilterPopover, + fillAddFilterForm, + openKqlQueryBar, + fillKqlQueryBar, +} from '../../tasks/search_bar'; +import { + AUTO_SUGGEST_AGENT_NAME, + AUTO_SUGGEST_HOST_NAME_VALUE, + GLOBAL_SEARCH_BAR_FILTER_ITEM, +} from '../../screens/search_bar'; import { getHostIpFilter } from '../../objects/filter'; import { HOSTS_URL } from '../../urls/navigation'; @@ -29,4 +38,11 @@ describe('SearchBar', () => { `${getHostIpFilter().key}: ${getHostIpFilter().value}` ); }); + + it('auto suggests fields from the data view and auto completes available field values', () => { + openKqlQueryBar(); + cy.get(AUTO_SUGGEST_AGENT_NAME).should('be.visible'); + fillKqlQueryBar(`host.name:`); + cy.get(AUTO_SUGGEST_HOST_NAME_VALUE).should('be.visible'); + }); }); diff --git a/x-pack/plugins/security_solution/cypress/screens/search_bar.ts b/x-pack/plugins/security_solution/cypress/screens/search_bar.ts index d5f3e7743c9b1..352a841a1c7be 100644 --- a/x-pack/plugins/security_solution/cypress/screens/search_bar.ts +++ b/x-pack/plugins/security_solution/cypress/screens/search_bar.ts @@ -32,3 +32,10 @@ export const GLOBAL_SEARCH_BAR_FILTER_ITEM = '#popoverFor_filter0'; export const GLOBAL_SEARCH_BAR_FILTER_ITEM_AT = (value: number) => `#popoverFor_filter${value}`; export const GLOBAL_SEARCH_BAR_PINNED_FILTER = '.globalFilterItem-isPinned'; + +export const GLOBAL_KQL_INPUT = + '[data-test-subj="filters-global-container"] [data-test-subj="unifiedQueryInput"] textarea'; + +export const AUTO_SUGGEST_AGENT_NAME = `[data-test-subj="autocompleteSuggestion-field-agent.name-"]`; + +export const AUTO_SUGGEST_HOST_NAME_VALUE = `[data-test-subj='autocompleteSuggestion-value-"siem-kibana"-']`; diff --git a/x-pack/plugins/security_solution/cypress/tasks/search_bar.ts b/x-pack/plugins/security_solution/cypress/tasks/search_bar.ts index 3a6a078ca1e54..fb5e978befdda 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/search_bar.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/search_bar.ts @@ -16,6 +16,7 @@ import { ADD_FILTER_FORM_OPERATOR_FIELD, ADD_FILTER_FORM_FIELD_OPTION, ADD_FILTER_FORM_FILTER_VALUE_INPUT, + GLOBAL_KQL_INPUT, } from '../screens/search_bar'; export const openAddFilterPopover = () => { @@ -24,6 +25,16 @@ export const openAddFilterPopover = () => { cy.get(GLOBAL_SEARCH_BAR_ADD_FILTER).click(); }; +export const openKqlQueryBar = () => { + cy.get(GLOBAL_KQL_INPUT).should('be.visible'); + cy.get(GLOBAL_KQL_INPUT).click(); +}; + +export const fillKqlQueryBar = (query: string) => { + cy.get(GLOBAL_KQL_INPUT).should('be.visible'); + cy.get(GLOBAL_KQL_INPUT).type(query); +}; + export const fillAddFilterForm = ({ key, value, operator }: SearchBarFilter) => { cy.get(ADD_FILTER_FORM_FIELD_INPUT).should('exist'); cy.get(ADD_FILTER_FORM_FIELD_INPUT).should('be.visible'); From ab8dd04073a4f9b8bcf26444f35a33d85f98b600 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Mon, 28 Nov 2022 18:52:15 +0200 Subject: [PATCH 06/11] [UA] fix flaky test (#146351) Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Closes https://github.com/elastic/kibana/issues/144885 --- .../functional/apps/upgrade_assistant/deprecation_pages.ts | 6 ++---- x-pack/test/functional/config.base.js | 6 +++++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/x-pack/test/functional/apps/upgrade_assistant/deprecation_pages.ts b/x-pack/test/functional/apps/upgrade_assistant/deprecation_pages.ts index 756fc43055f17..bf1794863a2d0 100644 --- a/x-pack/test/functional/apps/upgrade_assistant/deprecation_pages.ts +++ b/x-pack/test/functional/apps/upgrade_assistant/deprecation_pages.ts @@ -20,10 +20,8 @@ export default function upgradeAssistantFunctionalTests({ const security = getService('security'); const log = getService('log'); - // FLAKY: https://github.com/elastic/kibana/issues/144885 - describe.skip('Deprecation pages', function () { + describe('Deprecation pages', function () { this.tags('skipFirefox'); - this.timeout(32000); before(async () => { await security.testUser.setRoles(['superuser']); @@ -64,7 +62,7 @@ export default function upgradeAssistantFunctionalTests({ }); // Wait for the cluster settings to be reflected to the ES nodes - await setTimeout(30000); + await setTimeout(12000); } catch (e) { log.debug('[Setup error] Error updating cluster settings'); throw e; diff --git a/x-pack/test/functional/config.base.js b/x-pack/test/functional/config.base.js index 7ed28068485b3..8d4b51cee3603 100644 --- a/x-pack/test/functional/config.base.js +++ b/x-pack/test/functional/config.base.js @@ -36,7 +36,11 @@ export default async function ({ readConfigFile }) { esTestCluster: { license: 'trial', from: 'snapshot', - serverArgs: ['path.repo=/tmp/', 'xpack.security.authc.api_key.enabled=true'], + serverArgs: [ + 'path.repo=/tmp/', + 'xpack.security.authc.api_key.enabled=true', + 'cluster.routing.allocation.disk.threshold_enabled=true', // make sure disk thresholds are enabled for UA cluster testing + ], }, kbnTestServer: { From 0f31951235ecf11530c23977d028069f427bae67 Mon Sep 17 00:00:00 2001 From: Vitalii Dmyterko <92328789+vitaliidm@users.noreply.github.com> Date: Mon, 28 Nov 2022 17:26:07 +0000 Subject: [PATCH 07/11] [Security Solution][Alerts] fixes new terms multi fields timeline issue (#146185) ## Summary - fixes https://github.com/elastic/kibana/issues/146131 ### Before https://user-images.githubusercontent.com/92328789/203600097-589d4b8b-c238-48e4-bf72-2f2efcc39ac2.mp4 ### After https://user-images.githubusercontent.com/92328789/203599864-37a4ccc3-12b8-4c48-87ea-559d15582ef9.mov ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --- .../components/alerts_table/actions.test.tsx | 60 +++++++++++++++++++ .../components/alerts_table/actions.tsx | 45 ++++++++------ 2 files changed, 87 insertions(+), 18 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx index 4fee905feb479..fbd14853c88f5 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx @@ -7,6 +7,8 @@ import sinon from 'sinon'; import moment from 'moment'; +import set from 'lodash/set'; +import cloneDeep from 'lodash/cloneDeep'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import type { Filter } from '@kbn/es-query'; @@ -16,6 +18,7 @@ import { sendAlertToTimelineAction, sendBulkEventsToTimelineAction, determineToAndFrom, + getNewTermsData, } from './actions'; import { defaultTimelineProps, @@ -933,6 +936,63 @@ describe('alert actions', () => { }); }); + describe('New terms', () => { + describe('getNewTermsData', () => { + it('should return new terms data correctly for single value field', () => { + const newTermsEcsMock = cloneDeep(ecsDataMockWithNoTemplateTimeline[0]); + set(newTermsEcsMock, 'kibana.alert.new_terms', ['host-0']); + set(newTermsEcsMock, 'kibana.alert.rule.parameters.new_terms_fields', ['host.name']); + + expect(getNewTermsData(newTermsEcsMock).dataProviders).toEqual([ + { + and: [], + enabled: true, + excluded: false, + id: 'send-alert-to-timeline-action-default-draggable-event-details-value-formatted-field-value-timeline-1-host-name-host-0', + kqlQuery: '', + name: 'host.name', + queryMatch: { field: 'host.name', operator: ':', value: 'host-0' }, + }, + ]); + }); + + it('should return new terms data as AND query for multiple values field', () => { + const newTermsEcsMock = cloneDeep(ecsDataMockWithNoTemplateTimeline[0]); + set(newTermsEcsMock, 'kibana.alert.new_terms', ['host-0', '127.0.0.1']); + set(newTermsEcsMock, 'kibana.alert.rule.parameters.new_terms_fields', [ + 'host.name', + 'host.ip', + ]); + + expect(getNewTermsData(newTermsEcsMock).dataProviders).toEqual([ + { + and: [ + { + and: [], + enabled: true, + excluded: false, + id: 'send-alert-to-timeline-action-default-draggable-event-details-value-formatted-field-value-timeline-1-host-ip-127.0.0.1', + kqlQuery: '', + name: 'host.ip', + queryMatch: { + field: 'host.ip', + operator: ':', + value: '127.0.0.1', + }, + }, + ], + enabled: true, + excluded: false, + id: 'send-alert-to-timeline-action-default-draggable-event-details-value-formatted-field-value-timeline-1-host-name-host-0', + kqlQuery: '', + name: 'host.name', + queryMatch: { field: 'host.name', operator: ':', value: 'host-0' }, + }, + ]); + }); + }); + }); + describe('determineToAndFrom', () => { beforeEach(() => { fetchMock.mockResolvedValue({ diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx index aaa2a7ace106a..804289c9cdc7a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx @@ -560,29 +560,38 @@ const createThresholdTimeline = async ( } }; -const getNewTermsData = (ecsData: Ecs | Ecs[]) => { +export const getNewTermsData = (ecsData: Ecs | Ecs[]) => { const normalizedEcsData: Ecs = Array.isArray(ecsData) ? ecsData[0] : ecsData; const originalTimeValue = getField(normalizedEcsData, ALERT_ORIGINAL_TIME); - const newTermsField = getField(normalizedEcsData, `${ALERT_RULE_PARAMETERS}.new_terms_fields`)[0]; - const newTermsValue = getField(normalizedEcsData, ALERT_NEW_TERMS)[0]; - const newTermsFieldId = newTermsField.replace('.', '-'); - const dataProviderPartial = { - id: `send-alert-to-timeline-action-default-draggable-event-details-value-formatted-field-value-${TimelineId.active}-${newTermsFieldId}-${newTermsValue}`, - name: newTermsField, - enabled: true, - excluded: false, - kqlQuery: '', - queryMatch: { - field: newTermsField, - value: newTermsValue, - operator: ':' as const, - }, - and: [], - }; + const newTermsFields: string[] = + getField(normalizedEcsData, `${ALERT_RULE_PARAMETERS}.new_terms_fields`) ?? []; + + const dataProviderPartials = newTermsFields.map((newTermsField, index) => { + const newTermsFieldId = newTermsField.replace('.', '-'); + const newTermsValue = getField(normalizedEcsData, ALERT_NEW_TERMS)[index]; + return { + id: `send-alert-to-timeline-action-default-draggable-event-details-value-formatted-field-value-${TimelineId.active}-${newTermsFieldId}-${newTermsValue}`, + name: newTermsField, + enabled: true, + excluded: false, + kqlQuery: '', + queryMatch: { + field: newTermsField, + value: newTermsValue, + operator: ':' as const, + }, + and: [], + }; + }); + + const dataProviders = dataProviderPartials.length + ? [{ ...dataProviderPartials[0], and: dataProviderPartials.slice(1) }] + : []; + return { from: originalTimeValue, to: moment().toISOString(), - dataProviders: [dataProviderPartial], + dataProviders, }; }; From c086220f1ba89c9db0fe2c7500d86e3375aeee86 Mon Sep 17 00:00:00 2001 From: Vitalii Dmyterko <92328789+vitaliidm@users.noreply.github.com> Date: Mon, 28 Nov 2022 17:36:40 +0000 Subject: [PATCH 08/11] [Security Sollution][Alerts] fixes rule preview issue for new terms field (#145707) ## Summary - fixes https://github.com/elastic/kibana/issues/144322 - details on underlying [issue](https://github.com/elastic/kibana/issues/144322#issuecomment-1321838136) within form-lib ### Before https://user-images.githubusercontent.com/92328789/202687215-e9606bd0-5cfd-4a92-9abf-edaf90868505.mov ### After https://user-images.githubusercontent.com/92328789/202688418-7cb7d250-02f3-4020-bfa0-65191b8a529b.mov --- .../components/rules/rule_preview/helpers.ts | 7 +++- .../rules/step_define_rule/index.tsx | 32 ++++++++++--------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/helpers.ts b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/helpers.ts index 3a506a6c4d2c6..68de7404eb184 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/helpers.ts +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/helpers.ts @@ -15,6 +15,7 @@ import type { ChartSeriesConfigs } from '../../../../common/components/charts/co import type { FieldValueQueryBar } from '../query_bar'; import type { TimeframePreviewOptions } from '../../../pages/detection_engine/rules/types'; import { DataSourceType } from '../../../pages/detection_engine/rules/types'; +import { MAX_NUMBER_OF_NEW_TERMS_FIELDS } from '../../../../../common/constants'; /** * Determines whether or not to display noise warning. @@ -108,6 +109,10 @@ export const getHistogramConfig = ( }; }; +const isNewTermsPreviewDisabled = (newTermsFields: string[]): boolean => { + return newTermsFields.length === 0 || newTermsFields.length > MAX_NUMBER_OF_NEW_TERMS_FIELDS; +}; + export const getIsRulePreviewDisabled = ({ ruleType, isQueryBarValid, @@ -157,7 +162,7 @@ export const getIsRulePreviewDisabled = ({ return isEmpty(queryBar.query.query) && isEmpty(queryBar.filters); } if (ruleType === 'new_terms') { - return newTermsFields.length === 0; + return isNewTermsPreviewDisabled(newTermsFields); } return false; }; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx index e8fec6d0e9186..bdf7fb219b859 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx @@ -146,7 +146,7 @@ const StepDefineRuleComponent: FC = ({ schema, }); - const { getFields, getFormData, reset, submit } = form; + const { getFields, getFormData, reset, validate } = form; const [formData] = useFormData({ form, watch: [ @@ -392,21 +392,23 @@ const StepDefineRuleComponent: FC = ({ }, [onSubmit]); const getData = useCallback(async () => { - const result = await submit(); - result.data = { - ...result.data, - eqlOptions: optionsSelected, + // validate doesn't return actual state of form + // more details here: https://github.com/elastic/kibana/issues/144322#issuecomment-1321838136 + // wrapping in setTimeout is a workaround until solution within forms-lib can be found + const isValid = await new Promise((resolve) => { + setTimeout(async () => { + const valid = await validate(); + resolve(valid); + }, 0); + }); + return { + isValid, + data: { + ...getFormData(), + eqlOptions: optionsSelected, + }, }; - return result.isValid - ? result - : { - isValid: false, - data: { - ...getFormData(), - eqlOptions: optionsSelected, - }, - }; - }, [getFormData, optionsSelected, submit]); + }, [getFormData, optionsSelected, validate]); useEffect(() => { let didCancel = false; From 69c59a971adf7170013d782a1df675a28f262486 Mon Sep 17 00:00:00 2001 From: spalger Date: Mon, 28 Nov 2022 12:02:20 -0600 Subject: [PATCH 09/11] remove invalid JSX from docs --- packages/shared-ux/file/image/impl/README.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared-ux/file/image/impl/README.mdx b/packages/shared-ux/file/image/impl/README.mdx index fac2086a4e51c..2c1ee28e73b51 100644 --- a/packages/shared-ux/file/image/impl/README.mdx +++ b/packages/shared-ux/file/image/impl/README.mdx @@ -9,7 +9,7 @@ date: 2022-11-22 ## Description -A light wrapper around that introduces some "file-aware" functionality like displaying a [blurhash](https://blurha.sh/) before the final image loads. +A light wrapper around [EuiImage](https://elastic.github.io/eui/#/display/image) that introduces some "file-aware" functionality like displaying a [blurhash](https://blurha.sh/) before the final image loads. ## Lazy loading From 91ae3df37d549d57e69f63a28ab8b74fa944b44a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yulia=20=C4=8Cech?= <6585477+yuliacech@users.noreply.github.com> Date: Mon, 28 Nov 2022 19:03:03 +0100 Subject: [PATCH 10/11] [Guided onboarding] Fix API requests spam when request fails (#145728) --- src/plugins/guided_onboarding/common/types.ts | 9 ++++++++- .../guided_onboarding/public/services/api.test.ts | 4 ++-- src/plugins/guided_onboarding/public/services/api.ts | 8 +++++++- .../server/helpers/plugin_state_utils.ts | 9 +-------- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/plugins/guided_onboarding/common/types.ts b/src/plugins/guided_onboarding/common/types.ts index 74e0ed38f8142..f7f077c0d2b82 100644 --- a/src/plugins/guided_onboarding/common/types.ts +++ b/src/plugins/guided_onboarding/common/types.ts @@ -15,8 +15,15 @@ import { GuideState } from '@kbn/guided-onboarding'; * complete: at least one guide has been completed * quit: the user quit a guide before completion * skipped: the user skipped on the landing page + * error: unable to retrieve the plugin state from saved objects */ -export type PluginStatus = 'not_started' | 'in_progress' | 'complete' | 'quit' | 'skipped'; +export type PluginStatus = + | 'not_started' + | 'in_progress' + | 'complete' + | 'quit' + | 'skipped' + | 'error'; export interface PluginState { status: PluginStatus; diff --git a/src/plugins/guided_onboarding/public/services/api.test.ts b/src/plugins/guided_onboarding/public/services/api.test.ts index 2b39ca889ac95..db995002ddfcb 100644 --- a/src/plugins/guided_onboarding/public/services/api.test.ts +++ b/src/plugins/guided_onboarding/public/services/api.test.ts @@ -70,13 +70,13 @@ describe('GuidedOnboarding ApiService', () => { expect(httpClient.get).toHaveBeenCalledTimes(1); }); - it(`re-sends the request if the previous one failed`, async () => { + it(`doesn't send multiple requests if the request failed`, async () => { httpClient.get.mockRejectedValueOnce(new Error('request failed')); subscription = apiService.fetchPluginState$().subscribe(); // wait until the request fails await new Promise((resolve) => process.nextTick(resolve)); anotherSubscription = apiService.fetchPluginState$().subscribe(); - expect(httpClient.get).toHaveBeenCalledTimes(2); + expect(httpClient.get).toHaveBeenCalledTimes(1); }); it(`re-sends the request if the subscription was unsubscribed before the request completed`, async () => { diff --git a/src/plugins/guided_onboarding/public/services/api.ts b/src/plugins/guided_onboarding/public/services/api.ts index 94fe675d9fb06..3452e282b1680 100644 --- a/src/plugins/guided_onboarding/public/services/api.ts +++ b/src/plugins/guided_onboarding/public/services/api.ts @@ -55,7 +55,13 @@ export class ApiService implements GuidedOnboardingApi { }) .catch((error) => { this.isPluginStateLoading = false; - observer.error(error); + // if the request fails, we initialize the state with error + observer.next({ status: 'error', isActivePeriod: false }); + this.pluginState$.next({ + status: 'error', + isActivePeriod: false, + }); + observer.complete(); }); return () => { this.isPluginStateLoading = false; diff --git a/src/plugins/guided_onboarding/server/helpers/plugin_state_utils.ts b/src/plugins/guided_onboarding/server/helpers/plugin_state_utils.ts index 06fc211f1864f..f24fdf814f83b 100644 --- a/src/plugins/guided_onboarding/server/helpers/plugin_state_utils.ts +++ b/src/plugins/guided_onboarding/server/helpers/plugin_state_utils.ts @@ -40,14 +40,7 @@ export const getPluginState = async (savedObjectsClient: SavedObjectsClient) => return pluginState; } else { // create a SO to keep track of the correct creation date - try { - await updatePluginStatus(savedObjectsClient, 'not_started'); - // @yulia, we need to add a user permissions - // check here instead of swallowing this error - // see issue: https://github.com/elastic/kibana/issues/145434 - // eslint-disable-next-line no-empty - } catch (e) {} - + await updatePluginStatus(savedObjectsClient, 'not_started'); return { status: 'not_started', isActivePeriod: true, From a270d75f36207567f340a63b3666f9229deb7a7f Mon Sep 17 00:00:00 2001 From: Rashmi Kulkarni Date: Mon, 28 Nov 2022 10:08:50 -0800 Subject: [PATCH 11/11] fix for the skipped functional test `sync_colors.ts` (#146083) trying to fix https://github.com/elastic/kibana/issues/97403 Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../test/functional/apps/dashboard/group2/sync_colors.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/x-pack/test/functional/apps/dashboard/group2/sync_colors.ts b/x-pack/test/functional/apps/dashboard/group2/sync_colors.ts index 093318bb8b5cd..7e31cbcdc425a 100644 --- a/x-pack/test/functional/apps/dashboard/group2/sync_colors.ts +++ b/x-pack/test/functional/apps/dashboard/group2/sync_colors.ts @@ -33,9 +33,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { return colorMapping; } - - // FLAKY: https://github.com/elastic/kibana/issues/97403 - describe.skip('sync colors', function () { + describe('sync colors', function () { before(async function () { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); await kibanaServer.importExport.load( @@ -48,6 +46,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await kibanaServer.importExport.unload( 'x-pack/test/functional/fixtures/kbn_archiver/lens/lens_basic.json' ); + await kibanaServer.savedObjects.cleanStandardList(); }); it('should sync colors on dashboard by default', async function () { @@ -90,9 +89,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await filterBar.addFilter('geo.src', 'is not', 'CN'); await PageObjects.lens.save('vis2', false, true); + await PageObjects.dashboard.useColorSync(true); await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.dashboard.waitForRenderComplete(); + const colorMapping1 = getColorMapping(await PageObjects.dashboard.getPanelChartDebugState(0)); const colorMapping2 = getColorMapping(await PageObjects.dashboard.getPanelChartDebugState(1)); + expect(Object.keys(colorMapping1)).to.have.length(6); expect(Object.keys(colorMapping1)).to.have.length(6); const panel1Keys = ['CN'];