From 2ab5c2c40a61134d9d9e903ed46ea5a40a8b3fe8 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Thu, 12 Aug 2021 08:20:28 -0500 Subject: [PATCH 01/45] Revert "[RAC] integrating rbac search strategy with alert flyout (#107748)" This reverts commit e9ac0c6674d2979b48565452713bb5cf41d2dd88. --- .../cases/public/components/case_view/index.tsx | 5 +---- .../public/components/timeline_context/index.tsx | 8 +------- .../public/cases/components/case_view/index.tsx | 9 +-------- .../common/components/events_viewer/index.tsx | 6 ------ .../components/side_panel/event_details/index.tsx | 9 +-------- .../timelines/components/side_panel/index.tsx | 9 --------- .../components/timeline/eql_tab_content/index.tsx | 4 ---- .../timeline/notes_tab_content/index.tsx | 5 ----- .../timeline/pinned_tab_content/index.tsx | 4 ---- .../timeline/query_tab_content/index.tsx | 4 ---- .../public/timelines/containers/details/index.tsx | 15 +-------------- .../timelines/common/utils/field_formatters.ts | 2 +- .../timeline/factory/events/details/index.ts | 1 - 13 files changed, 6 insertions(+), 75 deletions(-) diff --git a/x-pack/plugins/cases/public/components/case_view/index.tsx b/x-pack/plugins/cases/public/components/case_view/index.tsx index b333d908fa77c..a44c2cb22010e 100644 --- a/x-pack/plugins/cases/public/components/case_view/index.tsx +++ b/x-pack/plugins/cases/public/components/case_view/index.tsx @@ -105,7 +105,6 @@ export const CaseComponent = React.memo( const [initLoadingData, setInitLoadingData] = useState(true); const init = useRef(true); const timelineUi = useTimelineContext()?.ui; - const alertConsumers = useTimelineContext()?.alertConsumers; const { caseUserActions, @@ -487,9 +486,7 @@ export const CaseComponent = React.memo( - {timelineUi?.renderTimelineDetailsPanel - ? timelineUi.renderTimelineDetailsPanel({ alertConsumers }) - : null} + {timelineUi?.renderTimelineDetailsPanel ? timelineUi.renderTimelineDetailsPanel() : null} ); } diff --git a/x-pack/plugins/cases/public/components/timeline_context/index.tsx b/x-pack/plugins/cases/public/components/timeline_context/index.tsx index 7e8def25f1d41..727e4b64628d1 100644 --- a/x-pack/plugins/cases/public/components/timeline_context/index.tsx +++ b/x-pack/plugins/cases/public/components/timeline_context/index.tsx @@ -7,7 +7,6 @@ import React, { useState } from 'react'; import { EuiMarkdownEditorUiPlugin, EuiMarkdownAstNodePosition } from '@elastic/eui'; -import type { AlertConsumers } from '@kbn/rule-data-utils/target/alerts_as_data_rbac'; import { Plugin } from 'unified'; /** * @description - manage the plugins, hooks, and ui components needed to enable timeline functionality within the cases plugin @@ -29,7 +28,6 @@ interface TimelineProcessingPluginRendererProps { } export interface CasesTimelineIntegration { - alertConsumers?: AlertConsumers[]; editor_plugins: { parsingPlugin: Plugin; processingPluginRenderer: React.FC< @@ -45,11 +43,7 @@ export interface CasesTimelineIntegration { }; ui?: { renderInvestigateInTimelineActionComponent?: (alertIds: string[]) => JSX.Element; - renderTimelineDetailsPanel?: ({ - alertConsumers, - }: { - alertConsumers?: AlertConsumers[]; - }) => JSX.Element; + renderTimelineDetailsPanel?: () => JSX.Element; }; } diff --git a/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx b/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx index 5517ab97ce715..fdb12170309c7 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx @@ -7,7 +7,6 @@ import React, { useCallback, useRef, useState } from 'react'; import { useDispatch } from 'react-redux'; -import { AlertConsumers } from '@kbn/rule-data-utils/target/alerts_as_data_rbac'; import { getCaseDetailsUrl, @@ -34,7 +33,6 @@ import { SpyRoute } from '../../../common/utils/route/spy_routes'; import * as timelineMarkdownPlugin from '../../../common/components/markdown_editor/plugins/timeline'; import { CaseDetailsRefreshContext } from '../../../common/components/endpoint/host_isolation/endpoint_host_isolation_cases_context'; import { getEndpointDetailsPath } from '../../../management/common/routing'; -import { EntityType } from '../../../timelines/containers/details'; interface Props { caseId: string; @@ -55,17 +53,13 @@ export interface CaseProps extends Props { updateCase: (newCase: Case) => void; } -const ALERT_CONSUMER: AlertConsumers[] = [AlertConsumers.SIEM]; - -const TimelineDetailsPanel = ({ alertConsumers }: { alertConsumers?: AlertConsumers[] }) => { +const TimelineDetailsPanel = () => { const { browserFields, docValueFields } = useSourcererScope(SourcererScopeName.detections); return ( @@ -234,7 +228,6 @@ export const CaseView = React.memo(({ caseId, subCaseId, userCanCrud }: Props) = showAlertDetails, subCaseId, timelineIntegration: { - alertConsumers: ALERT_CONSUMER, editor_plugins: { parsingPlugin: timelineMarkdownPlugin.parser, processingPluginRenderer: timelineMarkdownPlugin.renderer, diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx index 47fe63723faec..fe6c7e85e175d 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx @@ -11,7 +11,6 @@ import deepEqual from 'fast-deep-equal'; import styled from 'styled-components'; import { isEmpty } from 'lodash/fp'; -import { AlertConsumers } from '@kbn/rule-data-utils/target/alerts_as_data_rbac'; import { inputsModel, inputsSelectors, State } from '../../store'; import { inputsActions } from '../../store/actions'; import { ControlColumnProps, RowRenderer, TimelineId } from '../../../../common/types/timeline'; @@ -31,7 +30,6 @@ import { useKibana } from '../../lib/kibana'; import { defaultControlColumn } from '../../../timelines/components/timeline/body/control_columns'; import { EventsViewer } from './events_viewer'; import * as i18n from './translations'; -import { EntityType } from '../../../timelines/containers/details'; const EMPTY_CONTROL_COLUMNS: ControlColumnProps[] = []; const leadingControlColumns: ControlColumnProps[] = [ @@ -69,8 +67,6 @@ export interface OwnProps { type Props = OwnProps & PropsFromRedux; -const alertConsumers: AlertConsumers[] = [AlertConsumers.SIEM]; - /** * The stateful events viewer component is the highest level component that is utilized across the security_solution pages layer where * timeline is used BESIDES the flyout. The flyout makes use of the `EventsViewer` component which is a subcomponent here @@ -209,9 +205,7 @@ const StatefulEventsViewerComponent: React.FC = ({ = ({ - alertConsumers, browserFields, docValueFields, - entityType, expandedEvent, handleOnEventClosed, isFlyoutView, @@ -79,9 +74,7 @@ const EventDetailsPanelComponent: React.FC = ({ timelineId, }) => { const [loading, detailsData] = useTimelineEventsDetails({ - alertConsumers, docValueFields, - entityType, indexName: expandedEvent.indexName ?? '', eventId: expandedEvent.eventId ?? '', skip: !expandedEvent.eventId, diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.tsx index 28eb0fb8c9ce0..3e57ec2e039f5 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.tsx @@ -8,8 +8,6 @@ import React, { useCallback, useMemo } from 'react'; import { useDispatch } from 'react-redux'; import { EuiFlyout, EuiFlyoutProps } from '@elastic/eui'; -import { AlertConsumers } from '@kbn/rule-data-utils/target/alerts_as_data_rbac'; - import { timelineActions, timelineSelectors } from '../../store/timeline'; import { timelineDefaults } from '../../store/timeline/defaults'; import { BrowserFields, DocValueFields } from '../../../common/containers/source'; @@ -18,13 +16,10 @@ import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; import { EventDetailsPanel } from './event_details'; import { HostDetailsPanel } from './host_details'; import { NetworkDetailsPanel } from './network_details'; -import { EntityType } from '../../containers/details'; interface DetailsPanelProps { - alertConsumers?: AlertConsumers[]; browserFields: BrowserFields; docValueFields: DocValueFields[]; - entityType?: EntityType; handleOnPanelClosed?: () => void; isFlyoutView?: boolean; tabType?: TimelineTabs; @@ -38,10 +33,8 @@ interface DetailsPanelProps { */ export const DetailsPanel = React.memo( ({ - alertConsumers, browserFields, docValueFields, - entityType, handleOnPanelClosed, isFlyoutView, tabType, @@ -77,10 +70,8 @@ export const DetailsPanel = React.memo( panelSize = 'm'; visiblePanel = ( = ({ activeTab, columns, @@ -349,7 +346,6 @@ export const EqlTabContentComponent: React.FC = ({ = ({ timelineId } () => expandedDetail[TimelineTabs.notes]?.panelView ? ( React.ReactNode; rowRenderers: RowRenderer[]; @@ -269,7 +266,6 @@ export const PinnedTabContentComponent: React.FC = ({ theme.eui.paddingSizes.s}; `; -const alertConsumers: AlertConsumers[] = [AlertConsumers.SIEM]; - const isTimerangeSame = (prevProps: Props, nextProps: Props) => prevProps.end === nextProps.end && prevProps.start === nextProps.start && @@ -417,7 +414,6 @@ export const QueryTabContentComponent: React.FC = ({ { const myRequest = { ...(prevRequest ?? {}), - alertConsumers, docValueFields, - entityType, indexName, eventId, factoryQueryType: TimelineEventsQueries.details, @@ -127,7 +114,7 @@ export const useTimelineEventsDetails = ({ } return prevRequest; }); - }, [alertConsumers, docValueFields, entityType, eventId, indexName]); + }, [docValueFields, eventId, indexName]); useEffect(() => { timelineDetailsSearch(timelineDetailsRequest); diff --git a/x-pack/plugins/timelines/common/utils/field_formatters.ts b/x-pack/plugins/timelines/common/utils/field_formatters.ts index a48f03b90af6b..b436f8e616122 100644 --- a/x-pack/plugins/timelines/common/utils/field_formatters.ts +++ b/x-pack/plugins/timelines/common/utils/field_formatters.ts @@ -43,7 +43,7 @@ export const getDataFromSourceHits = ( category?: string, path?: string ): TimelineEventsDetailsItem[] => - Object.keys(sources ?? {}).reduce((accumulator, source) => { + Object.keys(sources).reduce((accumulator, source) => { const item: EventSource = get(source, sources); if (Array.isArray(item) || isString(item) || isNumber(item)) { const field = path ? `${path}.${source}` : source; diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/index.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/index.ts index c7e973483c07b..c82d9af938a98 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/index.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/index.ts @@ -39,7 +39,6 @@ export const timelineEventsDetails: TimelineFactory Date: Thu, 12 Aug 2021 08:34:15 -0500 Subject: [PATCH 02/45] [Security Solution] update host isolation support (#108230) --- .../common/endpoint/generate_data.ts | 8 +- .../service/host_isolation/utils.test.ts | 95 +++++++++++-------- .../endpoint/service/host_isolation/utils.ts | 71 ++++++++++---- .../common/endpoint/types/index.ts | 1 + .../components/host_isolation/helpers.ts | 15 ++- .../use_host_isolation_action.tsx | 12 ++- .../view/hooks/use_endpoint_action_items.tsx | 1 + 7 files changed, 138 insertions(+), 65 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts index b6d9e5f0f3695..94fc6be366beb 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts @@ -7,6 +7,7 @@ import uuid from 'uuid'; import seedrandom from 'seedrandom'; +import semverLte from 'semver/functions/lte'; import { assertNever } from '@kbn/std'; import { AlertEvent, @@ -261,6 +262,7 @@ interface HostInfo { state?: { isolation: boolean; }; + capabilities?: string[]; }; } @@ -456,10 +458,13 @@ export class EndpointDocGenerator extends BaseDataGenerator { private createHostData(): HostInfo { const hostName = this.randomHostname(); const isIsolated = this.randomBoolean(0.3); + const agentVersion = this.randomVersion(); + const minCapabilitiesVersion = '7.15.0'; + const capabilities = ['isolation']; return { agent: { - version: this.randomVersion(), + version: agentVersion, id: this.seededUUIDv4(), type: 'endpoint', }, @@ -488,6 +493,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { state: { isolation: isIsolated, }, + capabilities: semverLte(minCapabilitiesVersion, agentVersion) ? capabilities : [], }, }; } diff --git a/x-pack/plugins/security_solution/common/endpoint/service/host_isolation/utils.test.ts b/x-pack/plugins/security_solution/common/endpoint/service/host_isolation/utils.test.ts index 8983f1a99b0cd..8b72fe5deb8f6 100644 --- a/x-pack/plugins/security_solution/common/endpoint/service/host_isolation/utils.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/service/host_isolation/utils.test.ts @@ -9,58 +9,71 @@ import { isVersionSupported, isOsSupported, isIsolationSupported } from './utils describe('Host Isolation utils isVersionSupported', () => { test.each` - a | b | expected - ${'8.14.0'} | ${'7.13.0'} | ${true} - ${'7.14.0'} | ${'7.13.0'} | ${true} - ${'7.14.1'} | ${'7.14.0'} | ${true} - ${'8.14.0'} | ${'9.14.0'} | ${false} - ${'7.13.0'} | ${'7.14.0'} | ${false} - ${'7.14.0'} | ${'7.14.1'} | ${false} - ${'7.14.0'} | ${'7.14.0'} | ${true} - ${'7.14.0-SNAPSHOT'} | ${'7.14.0'} | ${true} - ${'7.14.0-SNAPSHOT-beta'} | ${'7.14.0'} | ${true} - ${'7.14.0-alpha'} | ${'7.14.0'} | ${true} - ${'8.0.0-SNAPSHOT'} | ${'7.14.0'} | ${true} - ${'8.0.0'} | ${'7.14.0'} | ${true} - `('should validate that version $a is compatible($expected) to $b', ({ a, b, expected }) => { - expect( - isVersionSupported({ - currentVersion: a, - minVersionRequired: b, - }) - ).toEqual(expected); - }); + currentVersion | minVersionRequired | expected + ${'8.14.0'} | ${'7.13.0'} | ${true} + ${'7.14.0'} | ${'7.13.0'} | ${true} + ${'7.14.1'} | ${'7.14.0'} | ${true} + ${'8.14.0'} | ${'9.14.0'} | ${false} + ${'7.13.0'} | ${'7.14.0'} | ${false} + ${'7.14.0'} | ${'7.14.1'} | ${false} + ${'7.14.0'} | ${'7.14.0'} | ${true} + ${'7.14.0-SNAPSHOT'} | ${'7.14.0'} | ${true} + ${'7.14.0-SNAPSHOT-beta'} | ${'7.14.0'} | ${true} + ${'7.14.0-alpha'} | ${'7.14.0'} | ${true} + ${'8.0.0-SNAPSHOT'} | ${'7.14.0'} | ${true} + ${'8.0.0'} | ${'7.14.0'} | ${true} + `( + 'should validate that version $a is compatible($expected) to $b', + ({ currentVersion, minVersionRequired, expected }) => { + expect( + isVersionSupported({ + currentVersion, + minVersionRequired, + }) + ).toEqual(expected); + } + ); }); describe('Host Isolation utils isOsSupported', () => { test.each` - a | b | expected - ${'linux'} | ${['macos', 'linux']} | ${true} - ${'linux'} | ${['macos', 'windows']} | ${false} - `('should validate that os $a is compatible($expected) to $b', ({ a, b, expected }) => { - expect( - isOsSupported({ - currentOs: a, - supportedOss: b, - }) - ).toEqual(expected); - }); + currentOs | supportedOss | expected + ${'linux'} | ${{ macos: true, linux: true }} | ${true} + ${'linux'} | ${{ macos: true, windows: true }} | ${false} + `( + 'should validate that os $a is compatible($expected) to $b', + ({ currentOs, supportedOss, expected }) => { + expect( + isOsSupported({ + currentOs, + supportedOss, + }) + ).toEqual(expected); + } + ); }); describe('Host Isolation utils isIsolationSupported', () => { test.each` - a | b | expected - ${'windows'} | ${'7.14.0'} | ${true} - ${'linux'} | ${'7.13.0'} | ${false} - ${'linux'} | ${'7.14.0'} | ${false} - ${'macos'} | ${'7.13.0'} | ${false} + osName | version | capabilities | expected + ${'windows'} | ${'7.14.0'} | ${[]} | ${true} + ${'linux'} | ${'7.13.0'} | ${['isolation']} | ${false} + ${'linux'} | ${'7.14.0'} | ${['isolation']} | ${false} + ${'macos'} | ${'7.13.0'} | ${['isolation']} | ${false} + ${'linux'} | ${'7.13.0'} | ${['isolation']} | ${false} + ${'windows'} | ${'7.15.0'} | ${[]} | ${false} + ${'macos'} | ${'7.15.0'} | ${[]} | ${false} + ${'linux'} | ${'7.15.0'} | ${['isolation']} | ${true} + ${'macos'} | ${'7.15.0'} | ${['isolation']} | ${true} + ${'linux'} | ${'7.16.0'} | ${['isolation']} | ${true} `( - 'should validate that os $a and version $b supports hostIsolation($expected)', - ({ a, b, expected }) => { + 'should validate that os $a, version $b, and capabilities $c supports hostIsolation($expected)', + ({ osName, version, capabilities, expected }) => { expect( isIsolationSupported({ - osName: a, - version: b, + osName, + version, + capabilities, }) ).toEqual(expected); } diff --git a/x-pack/plugins/security_solution/common/endpoint/service/host_isolation/utils.ts b/x-pack/plugins/security_solution/common/endpoint/service/host_isolation/utils.ts index fd0180b9146e7..d012ddfda15ba 100644 --- a/x-pack/plugins/security_solution/common/endpoint/service/host_isolation/utils.ts +++ b/x-pack/plugins/security_solution/common/endpoint/service/host_isolation/utils.ts @@ -4,39 +4,68 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import semverLt from 'semver/functions/lt'; +import semverLte from 'semver/functions/lte'; +import { ImmutableArray } from '../../types'; + +const minSupportedVersion = '7.14.0'; +const minCapabilitiesVersion = '7.15.0'; +const supportedOssMap = { + macos: true, + windows: true, +}; +const isolationCapability = 'isolation'; + +function parseSemver(semver: string) { + return semver.includes('-') ? semver.substring(0, semver.indexOf('-')) : semver; +} export const isVersionSupported = ({ currentVersion, - minVersionRequired, + minVersionRequired = minSupportedVersion, }: { currentVersion: string; - minVersionRequired: string; + minVersionRequired?: string; }) => { - const parsedCurrentVersion = currentVersion.includes('-') - ? currentVersion.substring(0, currentVersion.indexOf('-')) - : currentVersion; - - return ( - parsedCurrentVersion === minVersionRequired || - semverLt(minVersionRequired, parsedCurrentVersion) - ); + const parsedCurrentVersion = parseSemver(currentVersion); + return semverLte(minVersionRequired, parsedCurrentVersion); }; export const isOsSupported = ({ currentOs, - supportedOss, + supportedOss = supportedOssMap, }: { currentOs: string; - supportedOss: string[]; -}) => { - return supportedOss.some((os) => currentOs === os); -}; + supportedOss?: { [os: string]: boolean }; +}) => !!supportedOss[currentOs]; -export const isIsolationSupported = ({ osName, version }: { osName: string; version: string }) => { +function isCapabilitiesSupported(semver: string): boolean { + const parsedVersion = parseSemver(semver); + // capabilities is only available from 7.15+ + return semverLte(minCapabilitiesVersion, parsedVersion); +} + +function isIsolationSupportedCapabilities(capabilities: ImmutableArray = []): boolean { + return capabilities.includes(isolationCapability); +} + +// capabilities isn't introduced until 7.15 so check the OS for support +function isIsolationSupportedOS(osName: string): boolean { const normalizedOs = osName.toLowerCase(); - return ( - isOsSupported({ currentOs: normalizedOs, supportedOss: ['macos', 'windows'] }) && - isVersionSupported({ currentVersion: version, minVersionRequired: '7.14.0' }) - ); + return isOsSupported({ currentOs: normalizedOs }); +} + +export const isIsolationSupported = ({ + osName, + version, + capabilities, +}: { + osName: string; + version: string; + capabilities?: ImmutableArray; +}): boolean => { + if (!version || !isVersionSupported({ currentVersion: version })) return false; + + return isCapabilitiesSupported(version) + ? isIsolationSupportedCapabilities(capabilities) + : isIsolationSupportedOS(osName); }; diff --git a/x-pack/plugins/security_solution/common/endpoint/types/index.ts b/x-pack/plugins/security_solution/common/endpoint/types/index.ts index 5f92965c0e6ed..dde7f7799757c 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts @@ -525,6 +525,7 @@ export type HostMetadata = Immutable<{ */ isolation?: boolean; }; + capabilities?: string[]; }; agent: { id: string; diff --git a/x-pack/plugins/security_solution/public/detections/components/host_isolation/helpers.ts b/x-pack/plugins/security_solution/public/detections/components/host_isolation/helpers.ts index aa08db0a23669..453cd5c7006c2 100644 --- a/x-pack/plugins/security_solution/public/detections/components/host_isolation/helpers.ts +++ b/x-pack/plugins/security_solution/public/detections/components/host_isolation/helpers.ts @@ -8,6 +8,19 @@ import { find } from 'lodash/fp'; import type { TimelineEventsDetailsItem } from '../../../../common'; +export const getFieldValues = ( + { + category, + field, + }: { + category: string; + field: string; + }, + data: TimelineEventsDetailsItem[] | null +) => { + return find({ category, field }, data)?.values; +}; + export const getFieldValue = ( { category, @@ -18,6 +31,6 @@ export const getFieldValue = ( }, data: TimelineEventsDetailsItem[] | null ) => { - const currentField = find({ category, field }, data)?.values; + const currentField = getFieldValues({ category, field }, data); return currentField && currentField.length > 0 ? currentField[0] : ''; }; diff --git a/x-pack/plugins/security_solution/public/detections/components/host_isolation/use_host_isolation_action.tsx b/x-pack/plugins/security_solution/public/detections/components/host_isolation/use_host_isolation_action.tsx index d7e54e7f9900b..c36d222d23ba1 100644 --- a/x-pack/plugins/security_solution/public/detections/components/host_isolation/use_host_isolation_action.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/host_isolation/use_host_isolation_action.tsx @@ -12,7 +12,7 @@ import { useIsolationPrivileges } from '../../../common/hooks/endpoint/use_isola import { endpointAlertCheck } from '../../../common/utils/endpoint_alert_check'; import { useHostIsolationStatus } from '../../containers/detection_engine/alerts/use_host_isolation_status'; import { ISOLATE_HOST, UNISOLATE_HOST } from './translations'; -import { getFieldValue } from './helpers'; +import { getFieldValue, getFieldValues } from './helpers'; interface UseHostIsolationActionProps { closePopover: () => void; @@ -46,9 +46,19 @@ export const useHostIsolationAction = ({ [detailsData] ); + const hostCapabilities = useMemo( + () => + getFieldValues( + { category: 'Endpoint', field: 'Endpoint.capabilities' }, + detailsData + ) as string[], + [detailsData] + ); + const isolationSupported = isIsolationSupported({ osName: hostOsFamily, version: agentVersion, + capabilities: hostCapabilities, }); const { diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx index 03df5d2bcbac7..34423767578d9 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx @@ -41,6 +41,7 @@ export const useEndpointActionItems = ( const isolationSupported = isIsolationSupported({ osName: endpointMetadata.host.os.name, version: endpointMetadata.agent.version, + capabilities: endpointMetadata.Endpoint.capabilities, }); const { show, From f498b7aad2a16f12e62f518de688b1386e0e4088 Mon Sep 17 00:00:00 2001 From: Pablo Machado Date: Thu, 12 Aug 2021 16:28:46 +0200 Subject: [PATCH 03/45] Update Alert page header style (#108359) --- .../detection_engine/detection_engine.tsx | 23 ++++--------------- .../pages/detection_engine/translations.ts | 2 +- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx index 323ef93133e24..4fc8d3e9167c2 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx @@ -31,7 +31,6 @@ import { SiemSearchBar } from '../../../common/components/search_bar'; import { SecuritySolutionPageWrapper } from '../../../common/components/page_wrapper'; import { inputsSelectors } from '../../../common/store/inputs'; import { setAbsoluteRangeDatePicker } from '../../../common/store/inputs/actions'; -import { useAlertInfo } from '../../components/alerts_info'; import { AlertsTable } from '../../components/alerts_table'; import { NoApiIntegrationKeyCallOut } from '../../components/callouts/no_api_integration_callout'; import { AlertsHistogramPanel } from '../../components/alerts_kpis/alerts_histogram_panel'; @@ -42,7 +41,7 @@ import { DetectionEngineHeaderPage } from '../../components/detection_engine_hea import { useListsConfig } from '../../containers/detection_engine/lists/use_lists_config'; import { DetectionEngineUserUnauthenticated } from './detection_engine_user_unauthenticated'; import * as i18n from './translations'; -import { LinkButton } from '../../../common/components/links'; +import { LinkAnchor } from '../../../common/components/links'; import { useFormatUrl } from '../../../common/components/link_to'; import { useGlobalFullScreen } from '../../../common/containers/use_full_screen'; import { Display } from '../../../hosts/pages/display'; @@ -122,7 +121,6 @@ const DetectionEnginePageComponent: React.FC = ({ loading: listsConfigLoading, needsConfiguration: needsListsConfiguration, } = useListsConfig(); - const [lastAlerts] = useAlertInfo({}); const { formatUrl } = useFormatUrl(SecurityPageName.rules); const [showBuildingBlockAlerts, setShowBuildingBlockAlerts] = useState(false); const [showOnlyThreatIndicatorAlerts, setShowOnlyThreatIndicatorAlerts] = useState(false); @@ -281,27 +279,14 @@ const DetectionEnginePageComponent: React.FC = ({ data-test-subj="detectionsAlertsPage" > - - {i18n.LAST_ALERT} - {': '} - {lastAlerts} - - ) - } - title={i18n.PAGE_TITLE} - > - + {i18n.BUTTON_MANAGE_RULES} - + diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/translations.ts index 9475701baf2fd..96e423aff1658 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/translations.ts @@ -43,7 +43,7 @@ export const ALERT = i18n.translate('xpack.securitySolution.detectionEngine.aler export const BUTTON_MANAGE_RULES = i18n.translate( 'xpack.securitySolution.detectionEngine.buttonManageRules', { - defaultMessage: 'Manage detection rules', + defaultMessage: 'Manage rules', } ); From 6a51acc4085e3ee67de9c914bd6730fb36a98c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ece=20=C3=96zalp?= Date: Thu, 12 Aug 2021 10:34:06 -0400 Subject: [PATCH 04/45] [CTI][RAC] adds indicator match rule (#107977) --- .../rule_types/__mocks__/rule.ts | 5 +- .../rule_types/__mocks__/rule_type.ts | 10 +- .../rule_types/field_maps/cti.ts | 154 +++++++++++ .../lib/detection_engine/rule_types/index.ts | 1 + .../create_indicator_match_alert_type.test.ts | 260 ++++++++++++++++++ .../create_indicator_match_alert_type.ts | 99 +++++++ .../query/create_query_alert_type.ts | 19 +- .../scripts/create_rule_indicator_match.sh | 72 +++++ .../lib/detection_engine/rule_types/types.ts | 12 + .../security_solution/server/plugin.ts | 36 ++- 10 files changed, 634 insertions(+), 34 deletions(-) create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/field_maps/cti.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/scripts/create_rule_indicator_match.sh diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule.ts index 25e687050cf88..9d72d72e78b16 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule.ts @@ -5,7 +5,9 @@ * 2.0. */ -export const createRuleMock = () => ({ +import { RuleParams } from '../../schemas/rule_schemas'; + +export const createRuleMock = (params: Partial) => ({ actions: [], author: [], buildingBlockType: undefined, @@ -49,4 +51,5 @@ export const createRuleMock = () => ({ updatedAt: '2020-01-10T21:11:45.839Z', updatedBy: 'elastic', version: 1, + ...params, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts index 0fa2bcc270a16..151b1d8b2fb13 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts @@ -18,8 +18,12 @@ import { AlertAttributes } from '../../signals/types'; import { createRuleMock } from './rule'; import { listMock } from '../../../../../../lists/server/mocks'; import { ruleRegistryMocks } from '../../../../../../rule_registry/server/mocks'; +import { RuleParams } from '../../schemas/rule_schemas'; -export const createRuleTypeMocks = () => { +export const createRuleTypeMocks = ( + ruleType: string = 'query', + ruleParams: Partial = {} +) => { /* eslint-disable @typescript-eslint/no-explicit-any */ let alertExecutor: (...args: any[]) => Promise; @@ -43,7 +47,7 @@ export const createRuleTypeMocks = () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); mockSavedObjectsClient.get.mockResolvedValue({ id: 'de2f6a49-28a3-4794-bad7-0e9482e075f8', - type: 'query', + type: ruleType, references: [], attributes: { actions: [], @@ -57,7 +61,7 @@ export const createRuleTypeMocks = () => { interval: '30m', }, throttle: '', - params: createRuleMock(), + params: createRuleMock(ruleParams), }, } as SavedObject); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/field_maps/cti.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/field_maps/cti.ts new file mode 100644 index 0000000000000..daf54e4f7cf5c --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/field_maps/cti.ts @@ -0,0 +1,154 @@ +/* + * 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. + */ + +export const ctiFieldMap = { + 'threat.indicator': { + type: 'nested', + array: false, + required: false, + }, + 'threat.indicator.as.number': { + type: 'long', + array: false, + required: false, + }, + 'threat.indicator.as.organization.name': { + type: 'keyword', + array: false, + required: false, + }, + 'threat.indicator.confidence': { + type: 'keyword', + array: false, + required: false, + }, + 'threat.indicator.dataset': { + type: 'keyword', + array: false, + required: false, + }, + 'threat.indicator.description': { + type: 'keyword', + array: false, + required: false, + }, + 'threat.indicator.domain': { + type: 'keyword', + array: false, + required: false, + }, + 'threat.indicator.email.address': { + type: 'keyword', + array: false, + required: false, + }, + 'threat.indicator.first_seen': { + type: 'date', + array: false, + required: false, + }, + 'threat.indicator.geo.city_name': { + type: 'keyword', + array: false, + required: false, + }, + 'threat.indicator.geo.continent_name': { + type: 'keyword', + array: false, + required: false, + }, + 'threat.indicator.geo.country_iso_code': { + type: 'keyword', + array: false, + required: false, + }, + 'threat.indicator.geo.country_name': { + type: 'keyword', + array: false, + required: false, + }, + 'threat.indicator.geo.location': { + type: 'geo_point', + array: false, + required: false, + }, + 'threat.indicator.geo.name': { + type: 'keyword', + array: false, + required: false, + }, + 'threat.indicator.geo.region_iso_code': { + type: 'keyword', + array: false, + required: false, + }, + 'threat.indicator.geo.region_name': { + type: 'keyword', + array: false, + required: false, + }, + 'threat.indicator.ip': { + type: 'ip', + array: false, + required: false, + }, + 'threat.indicator.last_seen': { + type: 'date', + array: false, + required: false, + }, + 'threat.indicator.marking.tlp': { + type: 'keyword', + array: false, + required: false, + }, + 'threat.indicator.matched.atomic': { + type: 'keyword', + array: false, + required: false, + }, + 'threat.indicator.matched.field': { + type: 'keyword', + array: false, + required: false, + }, + 'threat.indicator.matched.type': { + type: 'keyword', + array: false, + required: false, + }, + 'threat.indicator.module': { + type: 'keyword', + array: false, + required: false, + }, + 'threat.indicator.port': { + type: 'long', + array: false, + required: false, + }, + 'threat.indicator.provider': { + type: 'keyword', + array: false, + required: false, + }, + 'threat.indicator.scanner_stats': { + type: 'long', + array: false, + required: false, + }, + 'threat.indicator.sightings': { + type: 'long', + array: false, + required: false, + }, + 'threat.indicator.type': { + type: 'keyword', + array: false, + required: false, + }, +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/index.ts index 1b1fe48be00e0..75252cc3d47ae 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/index.ts @@ -6,3 +6,4 @@ */ export { createQueryAlertType } from './query/create_query_alert_type'; +export { createIndicatorMatchAlertType } from './indicator_match/create_indicator_match_alert_type'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.test.ts new file mode 100644 index 0000000000000..50aff00191396 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.test.ts @@ -0,0 +1,260 @@ +/* + * 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 { v4 } from 'uuid'; + +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mocks'; + +import { allowedExperimentalValues } from '../../../../../common/experimental_features'; +import { createRuleTypeMocks } from '../__mocks__/rule_type'; +import { createIndicatorMatchAlertType } from './create_indicator_match_alert_type'; +import { sampleDocNoSortId } from '../../signals/__mocks__/es_results'; +import { CountResponse } from 'kibana/server'; +import { RuleParams } from '../../schemas/rule_schemas'; + +jest.mock('../utils/get_list_client', () => ({ + getListClient: jest.fn().mockReturnValue({ + listClient: { + getListItemIndex: jest.fn(), + }, + exceptionsClient: jest.fn(), + }), +})); + +jest.mock('../../signals/rule_status_service', () => ({ + ruleStatusServiceFactory: () => ({ + goingToRun: jest.fn(), + success: jest.fn(), + partialFailure: jest.fn(), + error: jest.fn(), + }), +})); + +describe('Indicator Match Alerts', () => { + const params: Partial = { + from: 'now-1m', + index: ['*'], + threatIndex: ['filebeat-*'], + threatLanguage: 'kuery', + threatMapping: [ + { + entries: [ + { + field: 'file.hash.md5', + type: 'mapping', + value: 'threatintel.indicator.file.hash.md5', + }, + ], + }, + ], + threatQuery: '*:*', + to: 'now', + type: 'threat_match', + }; + + it('does not send an alert when no events found', async () => { + const { services, dependencies, executor } = createRuleTypeMocks('threat_match', params); + const indicatorMatchAlertType = createIndicatorMatchAlertType({ + experimentalFeatures: allowedExperimentalValues, + indexAlias: 'alerts.security-alerts', + lists: dependencies.lists, + logger: dependencies.logger, + mergeStrategy: 'allFields', + ruleDataClient: dependencies.ruleDataClient, + ruleDataService: dependencies.ruleDataService, + version: '1.0.0', + }); + + dependencies.alerting.registerType(indicatorMatchAlertType); + + services.scopedClusterClient.asCurrentUser.search.mockReturnValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + hits: { + hits: [], + sequences: [], + events: [], + total: { + relation: 'eq', + value: 0, + }, + }, + took: 0, + timed_out: false, + _shards: { + failed: 0, + skipped: 0, + successful: 1, + total: 1, + }, + }) + ); + + await executor({ params }); + expect(dependencies.ruleDataClient.getWriter).not.toBeCalled(); + }); + + it('does not send an alert when no enrichments are found', async () => { + const { services, dependencies, executor } = createRuleTypeMocks('threat_match', params); + const indicatorMatchAlertType = createIndicatorMatchAlertType({ + experimentalFeatures: allowedExperimentalValues, + indexAlias: 'alerts.security-alerts', + lists: dependencies.lists, + logger: dependencies.logger, + mergeStrategy: 'allFields', + ruleDataClient: dependencies.ruleDataClient, + ruleDataService: dependencies.ruleDataService, + version: '1.0.0', + }); + + dependencies.alerting.registerType(indicatorMatchAlertType); + + services.scopedClusterClient.asCurrentUser.search.mockReturnValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + hits: { + hits: [sampleDocNoSortId(v4()), sampleDocNoSortId(v4()), sampleDocNoSortId(v4())], + total: { + relation: 'eq', + value: 3, + }, + }, + took: 0, + timed_out: false, + _shards: { + failed: 0, + skipped: 0, + successful: 1, + total: 1, + }, + }) + ); + + await executor({ params }); + expect(dependencies.ruleDataClient.getWriter).not.toBeCalled(); + }); + + it('sends an alert when enrichments are found', async () => { + const { services, dependencies, executor } = createRuleTypeMocks('threat_match', params); + const indicatorMatchAlertType = createIndicatorMatchAlertType({ + experimentalFeatures: allowedExperimentalValues, + indexAlias: 'alerts.security-alerts', + lists: dependencies.lists, + logger: dependencies.logger, + mergeStrategy: 'allFields', + ruleDataClient: dependencies.ruleDataClient, + ruleDataService: dependencies.ruleDataService, + version: '1.0.0', + }); + + dependencies.alerting.registerType(indicatorMatchAlertType); + + // threat list count + services.scopedClusterClient.asCurrentUser.count.mockReturnValue( + elasticsearchClientMock.createSuccessTransportRequestPromise({ count: 1 } as CountResponse) + ); + + services.scopedClusterClient.asCurrentUser.search.mockReturnValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + hits: { + hits: [ + { + ...sampleDocNoSortId(v4()), + _source: { + ...sampleDocNoSortId(v4())._source, + 'threatintel.indicator.file.hash.md5': 'a1b2c3', + }, + fields: { + ...sampleDocNoSortId(v4()).fields, + 'threatintel.indicator.file.hash.md5': ['a1b2c3'], + }, + }, + ], + total: { + relation: 'eq', + value: 1, + }, + }, + took: 0, + timed_out: false, + _shards: { + failed: 0, + skipped: 0, + successful: 1, + total: 1, + }, + }) + ); + + services.scopedClusterClient.asCurrentUser.search.mockReturnValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + hits: { + hits: [ + { + ...sampleDocNoSortId(v4()), + _source: { + ...sampleDocNoSortId(v4())._source, + 'file.hash.md5': 'a1b2c3', + }, + fields: { + ...sampleDocNoSortId(v4()).fields, + 'file.hash.md5': ['a1b2c3'], + }, + }, + ], + total: { + relation: 'eq', + value: 1, + }, + }, + took: 0, + timed_out: false, + _shards: { + failed: 0, + skipped: 0, + successful: 1, + total: 1, + }, + }) + ); + + services.scopedClusterClient.asCurrentUser.search.mockReturnValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + hits: { + hits: [ + { + ...sampleDocNoSortId(v4()), + _source: { + ...sampleDocNoSortId(v4())._source, + 'file.hash.md5': 'a1b2c3', + }, + fields: { + ...sampleDocNoSortId(v4()).fields, + 'file.hash.md5': ['a1b2c3'], + }, + }, + ], + total: { + relation: 'eq', + value: 1, + }, + }, + took: 0, + timed_out: false, + _shards: { + failed: 0, + skipped: 0, + successful: 1, + total: 1, + }, + }) + ); + + await executor({ params }); + + expect(dependencies.ruleDataClient.getWriter).toBeCalled(); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts new file mode 100644 index 0000000000000..61342981479ae --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts @@ -0,0 +1,99 @@ +/* + * 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 { validateNonExact } from '@kbn/securitysolution-io-ts-utils'; +import { PersistenceServices } from '../../../../../../rule_registry/server'; +import { INDICATOR_ALERT_TYPE_ID } from '../../../../../common/constants'; +import { threatRuleParams, ThreatRuleParams } from '../../schemas/rule_schemas'; +import { threatMatchExecutor } from '../../signals/executors/threat_match'; +import { createSecurityRuleTypeFactory } from '../create_security_rule_type_factory'; +import { CreateRuleOptions } from '../types'; + +export const createIndicatorMatchAlertType = (createOptions: CreateRuleOptions) => { + const { + experimentalFeatures, + indexAlias, + lists, + logger, + mergeStrategy, + ruleDataClient, + version, + ruleDataService, + } = createOptions; + const createSecurityRuleType = createSecurityRuleTypeFactory({ + indexAlias, + lists, + logger, + mergeStrategy, + ruleDataClient, + ruleDataService, + }); + return createSecurityRuleType({ + id: INDICATOR_ALERT_TYPE_ID, + name: 'Indicator Match Rule', + validate: { + params: { + validate: (object: unknown) => { + const [validated, errors] = validateNonExact(object, threatRuleParams); + if (errors != null) { + throw new Error(errors); + } + if (validated == null) { + throw new Error('Validation of rule params failed'); + } + return validated; + }, + }, + }, + actionGroups: [ + { + id: 'default', + name: 'Default', + }, + ], + defaultActionGroupId: 'default', + actionVariables: { + context: [{ name: 'server', description: 'the server' }], + }, + minimumLicenseRequired: 'basic', + isExportable: false, + producer: 'security-solution', + async executor(execOptions) { + const { + runOpts: { + buildRuleMessage, + bulkCreate, + exceptionItems, + listClient, + rule, + searchAfterSize, + tuple, + wrapHits, + }, + services, + state, + } = execOptions; + + const result = await threatMatchExecutor({ + buildRuleMessage, + bulkCreate, + exceptionItems, + experimentalFeatures, + eventsTelemetry: undefined, + listClient, + logger, + rule, + searchAfterSize, + services, + tuple, + version, + wrapHits, + }); + return { ...result, state }; + }, + }); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts index c487d8c93119d..b66e76f598bf4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts @@ -5,28 +5,15 @@ * 2.0. */ -import { Logger } from '@kbn/logging'; import { validateNonExact } from '@kbn/securitysolution-io-ts-utils'; -import { PersistenceServices, RuleDataClient } from '../../../../../../rule_registry/server'; +import { PersistenceServices } from '../../../../../../rule_registry/server'; import { QUERY_ALERT_TYPE_ID } from '../../../../../common/constants'; -import { ExperimentalFeatures } from '../../../../../common/experimental_features'; -import { ConfigType } from '../../../../config'; -import { SetupPlugins } from '../../../../plugin'; -import { IRuleDataPluginService } from '../../rule_execution_log/types'; import { queryRuleParams, QueryRuleParams } from '../../schemas/rule_schemas'; import { queryExecutor } from '../../signals/executors/query'; import { createSecurityRuleTypeFactory } from '../create_security_rule_type_factory'; +import { CreateRuleOptions } from '../types'; -export const createQueryAlertType = (createOptions: { - experimentalFeatures: ExperimentalFeatures; - indexAlias: string; - lists: SetupPlugins['lists']; - logger: Logger; - mergeStrategy: ConfigType['alertMergeStrategy']; - ruleDataClient: RuleDataClient; - version: string; - ruleDataService: IRuleDataPluginService; -}) => { +export const createQueryAlertType = (createOptions: CreateRuleOptions) => { const { experimentalFeatures, indexAlias, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/scripts/create_rule_indicator_match.sh b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/scripts/create_rule_indicator_match.sh new file mode 100644 index 0000000000000..f50aac30a69c5 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/scripts/create_rule_indicator_match.sh @@ -0,0 +1,72 @@ +#!/bin/sh +# +# 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. +# + +# to see alerts with this script +# 1. enable filebeat threatintel module and run filebeat +# 2. using Kibana DevTools, create `test-index` with the following mappings: @timestamp:date, file.hash.md5:text +# 3. run this script +# 4. using Kibana DevTools, post to test-index an existing file.hash.md5 value with a current timestamp +# 5. alert should be generated and can be viewed on the Alerts table + +curl -X POST ${KIBANA_URL}${SPACE_URL}/api/alerts/alert \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -H 'kbn-xsrf: true' \ + -H 'Content-Type: application/json' \ + --verbose \ + -d ' +{ + "params":{ + "author": [], + "description": "Indicator Match Rule", + "exceptionsList": [], + "falsePositives": [], + "from": "now-300s", + "query": "*:*", + "immutable": false, + "index": ["test-*"], + "language": "kuery", + "maxSignals": 10, + "outputIndex": "", + "references": [], + "riskScore": 21, + "riskScoreMapping": [], + "ruleId": "81dec1ba-b779-469c-9667-6b0e865fb86c", + "severity": "low", + "severityMapping": [], + "threat": [], + "threatQuery": "*:*", + "threatMapping": [ + { + "entries":[ + { + "field":"file.hash.md5", + "type":"mapping", + "value":"threatintel.indicator.file.hash.md5" + } + ] + } + ], + "threatLanguage": "kuery", + "threatIndex": ["filebeat-*"], + "to": "now", + "type": "threat_match", + "version": 1 + }, + "consumer":"alerts", + "alertTypeId":"siem.indicatorRule", + "schedule":{ + "interval":"1m" + }, + "actions":[], + "tags":[ + "indicator match", + "persistence" + ], + "notifyWhen":"onActionGroupChange", + "name":"Basic Indicator Match Rule" +}' diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts index 20fb721308600..f0f11f470bc6c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts @@ -33,6 +33,7 @@ import { RuleParams } from '../schemas/rule_schemas'; import { BuildRuleMessage } from '../signals/rule_messages'; import { AlertAttributes, BulkCreate, WrapHits } from '../signals/types'; import { AlertsFieldMap, RulesFieldMap } from './field_maps'; +import { ExperimentalFeatures } from '../../../../common/experimental_features'; export interface SecurityAlertTypeReturnValue { bulkCreateTimes: string[]; @@ -118,3 +119,14 @@ export type RACAlert = Exclude< export type RACSourceHit = SearchHit; export type WrappedRACAlert = BaseHit; + +export interface CreateRuleOptions { + experimentalFeatures: ExperimentalFeatures; + indexAlias: string; + lists: SetupPlugins['lists']; + logger: Logger; + mergeStrategy: ConfigType['alertMergeStrategy']; + ruleDataClient: RuleDataClient; + version: string; + ruleDataService: IRuleDataPluginService; +} diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index e67c9fbfd8327..d19c799295cfb 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -66,6 +66,7 @@ import { NOTIFICATIONS_ID, QUERY_ALERT_TYPE_ID, DEFAULT_SPACE_ID, + INDICATOR_ALERT_TYPE_ID, } from '../common/constants'; import { registerEndpointRoutes } from './endpoint/routes/metadata'; import { registerLimitedConcurrencyRoutes } from './endpoint/routes/limited_concurrency'; @@ -94,6 +95,9 @@ import { rulesFieldMap } from './lib/detection_engine/rule_types/field_maps/rule import { RuleExecutionLogClient } from './lib/detection_engine/rule_execution_log/rule_execution_log_client'; import { getKibanaPrivilegesFeaturePrivileges } from './features'; import { EndpointMetadataService } from './endpoint/services/metadata'; +import { createIndicatorMatchAlertType } from './lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type'; +import { CreateRuleOptions } from './lib/detection_engine/rule_types/types'; +import { ctiFieldMap } from './lib/detection_engine/rule_types/field_maps/cti'; export interface SetupPlugins { alerting: AlertingSetup; @@ -231,7 +235,10 @@ export class Plugin implements IPlugin Date: Thu, 12 Aug 2021 10:40:21 -0400 Subject: [PATCH 05/45] Include additional functional test dirs in uptime CODEOWNERS entries. (#108368) --- .github/CODEOWNERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 95c11b05a9783..26616ab237660 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -107,6 +107,8 @@ /x-pack/plugins/observability/public/components/shared/exploratory_view @elastic/uptime /x-pack/test/functional_with_es_ssl/apps/uptime @elastic/uptime /x-pack/test/functional/apps/uptime @elastic/uptime +/x-pack/test/functional/es_archives/uptime @elastic/uptime +/x-pack/test/functional/services/uptime @elastic/uptime /x-pack/test/api_integration/apis/uptime @elastic/uptime # Client Side Monitoring / Uptime (lives in APM directories but owned by Uptime) From 338f7c5d7f17e4db6eee197d20dd2bc239b2b1ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loix?= Date: Thu, 12 Aug 2021 16:00:29 +0100 Subject: [PATCH 06/45] [Monaco editor]: Fix accessibility focus trap issue (#107292) --- .../__snapshots__/code_editor.test.tsx.snap | 273 ++++++++++-- .../code_editor/code_editor.test.helpers.tsx | 105 +++++ .../public/code_editor/code_editor.test.tsx | 196 ++++++--- .../public/code_editor/code_editor.tsx | 407 +++++++++++++----- .../public/code_editor/editor.scss | 21 + .../expression_input.stories.storyshot | 39 +- 6 files changed, 826 insertions(+), 215 deletions(-) create mode 100644 src/plugins/kibana_react/public/code_editor/code_editor.test.helpers.tsx diff --git a/src/plugins/kibana_react/public/code_editor/__snapshots__/code_editor.test.tsx.snap b/src/plugins/kibana_react/public/code_editor/__snapshots__/code_editor.test.tsx.snap index 230d31ac9e0b6..7b2729d2e1b68 100644 --- a/src/plugins/kibana_react/public/code_editor/__snapshots__/code_editor.test.tsx.snap +++ b/src/plugins/kibana_react/public/code_editor/__snapshots__/code_editor.test.tsx.snap @@ -1,52 +1,241 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`is rendered 1`] = ` - - hint element should be tabable 1`] = ` +
+`; + +exports[` is rendered 1`] = ` + +
+ +

+ + Enter + , + } + } + /> +

+

+ + Esc + , + } + } + /> +

+ + } + delay="regular" + display="block" + position="top" + > + +
+ + + - - + > +
+