From e628ae5b54fcaf240415864a1de3fda4b4b82f75 Mon Sep 17 00:00:00 2001 From: Katerina Patticha Date: Tue, 29 Mar 2022 09:46:14 +0200 Subject: [PATCH 01/37] [APM] Add loading state for trace samples (#128268) * Add loading state for trace samples * Fix message when there are no traces * Remove unused status * Use traces samples status to display loading indicator * Clean up code * Fix tests --- .../distribution/index.test.tsx | 2 + .../distribution/index.tsx | 8 +- .../transaction_details/trace_samples_tab.tsx | 2 + .../transaction_details_tabs.tsx | 12 +- .../app/transaction_details/types.ts | 2 + .../waterfall_with_summary/index.tsx | 72 ++++++----- .../maybe_view_trace_link.tsx | 114 ++++++++++-------- .../waterfall/transaction_flyout/index.tsx | 5 +- .../custom_link_list.tsx | 4 +- .../custom_link_menu_section/index.tsx | 2 +- .../transaction_action_menu/sections.ts | 3 +- .../transaction_action_menu.test.tsx | 7 +- .../transaction_action_menu.tsx | 108 ++++++++++------- 13 files changed, 197 insertions(+), 144 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.test.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.test.tsx index 1e4d1816bf84a..e2530458ae980 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.test.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.test.tsx @@ -100,6 +100,7 @@ describe('transaction_details/distribution', () => { onChartSelection={jest.fn()} onClearSelection={jest.fn()} traceSamples={[]} + traceSamplesStatus={useFetcherModule.FETCH_STATUS.LOADING} />, { wrapper: Wrapper } @@ -124,6 +125,7 @@ describe('transaction_details/distribution', () => { onChartSelection={jest.fn()} onClearSelection={jest.fn()} traceSamples={[]} + traceSamplesStatus={useFetcherModule.FETCH_STATUS.SUCCESS} /> ); diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.tsx index 7d387a39f9334..ca2a3584c8942 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.tsx @@ -59,6 +59,7 @@ interface TransactionDistributionProps { onClearSelection: () => void; selection?: Selection; traceSamples: TabContentProps['traceSamples']; + traceSamplesStatus: FETCH_STATUS; } export function TransactionDistribution({ @@ -66,10 +67,15 @@ export function TransactionDistribution({ onClearSelection, selection, traceSamples, + traceSamplesStatus, }: TransactionDistributionProps) { const { urlParams } = useLegacyUrlParams(); const { waterfall, status: waterfallStatus } = useWaterfallFetcher(); + const isLoading = + waterfallStatus === FETCH_STATUS.LOADING || + traceSamplesStatus === FETCH_STATUS.LOADING; + const markerCurrentTransaction = waterfall.entryWaterfallTransaction?.doc.transaction.duration.us; @@ -189,7 +195,7 @@ export function TransactionDistribution({ diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/trace_samples_tab.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/trace_samples_tab.tsx index ad629b7a2d132..d83211a72ea59 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/trace_samples_tab.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/trace_samples_tab.tsx @@ -18,6 +18,7 @@ function TraceSamplesTab({ sampleRangeFrom, sampleRangeTo, traceSamples, + traceSamplesStatus, }: TabContentProps) { return ( ); diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/transaction_details_tabs.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/transaction_details_tabs.tsx index 5e9f1f8149d9d..b0b5d495d1f8e 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/transaction_details_tabs.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/transaction_details_tabs.tsx @@ -41,11 +41,12 @@ export function TransactionDetailsTabs() { tabs.find((tab) => tab.key === currentTab) ?? traceSamplesTab; const { environment, kuery, transactionName } = query; - const { traceSamplesData } = useTransactionTraceSamplesFetcher({ - transactionName, - kuery, - environment, - }); + const { traceSamplesData, traceSamplesStatus } = + useTransactionTraceSamplesFetcher({ + transactionName, + kuery, + environment, + }); const selectSampleFromChartSelection = (selection: XYBrushEvent) => { if (selection !== undefined) { @@ -139,6 +140,7 @@ export function TransactionDetailsTabs() { sampleRangeTo, selectSampleFromChartSelection, traceSamples, + traceSamplesStatus, }} /> diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/types.ts b/x-pack/plugins/apm/public/components/app/transaction_details/types.ts index c3d2b9648e82a..2014f7588293d 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/types.ts +++ b/x-pack/plugins/apm/public/components/app/transaction_details/types.ts @@ -8,6 +8,7 @@ import { XYBrushEvent } from '@elastic/charts'; import type { TraceSample } from '../../../hooks/use_transaction_trace_samples_fetcher'; +import { FETCH_STATUS } from '../../../hooks/use_fetcher'; export interface TabContentProps { clearChartSelection: () => void; @@ -16,4 +17,5 @@ export interface TabContentProps { sampleRangeTo?: number; selectSampleFromChartSelection: (selection: XYBrushEvent) => void; traceSamples: TraceSample[]; + traceSamplesStatus: FETCH_STATUS; } diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/index.tsx index f528ce17c02f0..93913aff6cb6b 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/index.tsx @@ -65,10 +65,9 @@ export function WaterfallWithSummary({ }; const { entryWaterfallTransaction } = waterfall; - if (!entryWaterfallTransaction) { - const content = isLoading ? ( - - ) : ( + + if ((!entryWaterfallTransaction || traceSamples.length === 0) && !isLoading) { + return ( @@ -80,11 +79,9 @@ export function WaterfallWithSummary({ titleSize="s" /> ); - - return content; } - const entryTransaction = entryWaterfallTransaction.doc; + const entryTransaction = entryWaterfallTransaction?.doc; return ( <> @@ -99,43 +96,52 @@ export function WaterfallWithSummary({ - {traceSamples && ( - - )} + - + + + + - - - - - + {isLoading || !entryTransaction ? ( + + ) : ( + <> + + + + + )} ); } diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/maybe_view_trace_link.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/maybe_view_trace_link.tsx index 90fdaddc71408..ecb9fb9a3bc39 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/maybe_view_trace_link.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/maybe_view_trace_link.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiButton, EuiFlexItem, EuiToolTip } from '@elastic/eui'; +import { EuiButton, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { getNextEnvironmentUrlParam } from '../../../../../common/environment_filter_values'; @@ -15,12 +15,35 @@ import { IWaterfall } from './waterfall_container/waterfall/waterfall_helpers/wa import { Environment } from '../../../../../common/environment_rt'; import { useApmParams } from '../../../../hooks/use_apm_params'; +function FullTraceButton({ + isLoading, + isDisabled, +}: { + isLoading?: boolean; + isDisabled?: boolean; +}) { + return ( + + {i18n.translate('xpack.apm.transactionDetails.viewFullTraceButtonLabel', { + defaultMessage: 'View full trace', + })} + + ); +} + export function MaybeViewTraceLink({ + isLoading, transaction, waterfall, environment, }: { - transaction: ITransaction; + isLoading: boolean; + transaction?: ITransaction; waterfall: IWaterfall; environment: Environment; }) { @@ -28,31 +51,24 @@ export function MaybeViewTraceLink({ query: { latencyAggregationType, comparisonEnabled, offset }, } = useApmParams('/services/{serviceName}/transactions/view'); - const viewFullTraceButtonLabel = i18n.translate( - 'xpack.apm.transactionDetails.viewFullTraceButtonLabel', - { - defaultMessage: 'View full trace', - } - ); + if (isLoading || !transaction) { + return ; + } const { rootTransaction } = waterfall; // the traceroot cannot be found, so we cannot link to it if (!rootTransaction) { return ( - - - - {viewFullTraceButtonLabel} - - - + + + ); } @@ -61,20 +77,16 @@ export function MaybeViewTraceLink({ // the user is already viewing the full trace, so don't link to it if (isRoot) { return ( - - - - {viewFullTraceButtonLabel} - - - + + + ); // the user is viewing a zoomed in version of the trace. Link to the full trace @@ -85,23 +97,19 @@ export function MaybeViewTraceLink({ }); return ( - - - - {viewFullTraceButtonLabel} - - - + + + ); } } diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/transaction_flyout/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/transaction_flyout/index.tsx index 43a7ebfa2f97f..fd68fde81fb60 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/transaction_flyout/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/transaction_flyout/index.tsx @@ -76,7 +76,10 @@ export function TransactionFlyout({ - + diff --git a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/custom_link_menu_section/custom_link_list.tsx b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/custom_link_menu_section/custom_link_list.tsx index 7ea7cd35c443a..5cd7188c8ba8b 100644 --- a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/custom_link_menu_section/custom_link_list.tsx +++ b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/custom_link_menu_section/custom_link_list.tsx @@ -20,7 +20,7 @@ export function CustomLinkList({ transaction, }: { customLinks: CustomLink[]; - transaction: Transaction; + transaction?: Transaction; }) { return ( @@ -39,7 +39,7 @@ export function CustomLinkList({ ); } -function getHref(link: CustomLink, transaction: Transaction) { +function getHref(link: CustomLink, transaction?: Transaction) { try { return Mustache.render(link.url, transaction); } catch (e) { diff --git a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/custom_link_menu_section/index.tsx b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/custom_link_menu_section/index.tsx index aa3445d098c0e..68442ede98347 100644 --- a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/custom_link_menu_section/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/custom_link_menu_section/index.tsx @@ -41,7 +41,7 @@ const DEFAULT_LINKS_TO_SHOW = 3; export function CustomLinkMenuSection({ transaction, }: { - transaction: Transaction; + transaction?: Transaction; }) { const [showAllLinks, setShowAllLinks] = useState(false); const [isCreateEditFlyoutOpen, setIsCreateEditFlyoutOpen] = useState(false); diff --git a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/sections.ts b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/sections.ts index 20c10b6d9557c..8fbf0ea1f3235 100644 --- a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/sections.ts +++ b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/sections.ts @@ -35,11 +35,12 @@ export const getSections = ({ location, urlParams, }: { - transaction: Transaction; + transaction?: Transaction; basePath: IBasePath; location: Location; urlParams: ApmUrlParams; }) => { + if (!transaction) return []; const hostName = transaction.host?.hostname; const podId = transaction.kubernetes?.pod?.uid; const containerId = transaction.container?.id; diff --git a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/transaction_action_menu.test.tsx b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/transaction_action_menu.test.tsx index f2919fc12cad6..ebdbf759bcddc 100644 --- a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/transaction_action_menu.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/transaction_action_menu.test.tsx @@ -47,7 +47,10 @@ function Wrapper({ children }: { children?: React.ReactNode }) { const renderTransaction = async (transaction: Record) => { const rendered = render( - , + , { wrapper: Wrapper, } @@ -271,6 +274,7 @@ describe('TransactionActionMenu component', () => { return render( , @@ -308,6 +312,7 @@ describe('TransactionActionMenu component', () => { const component = render( , diff --git a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/transaction_action_menu.tsx b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/transaction_action_menu.tsx index 2f856dce387bf..253c792246139 100644 --- a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/transaction_action_menu.tsx +++ b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/transaction_action_menu.tsx @@ -26,12 +26,24 @@ import { CustomLinkMenuSection } from './custom_link_menu_section'; import { getSections } from './sections'; interface Props { - readonly transaction: Transaction; + readonly transaction?: Transaction; + isLoading: boolean; } -function ActionMenuButton({ onClick }: { onClick: () => void }) { +function ActionMenuButton({ + onClick, + isLoading, +}: { + onClick: () => void; + isLoading: boolean; +}) { return ( - + {i18n.translate('xpack.apm.transactionActionMenu.actionsButtonLabel', { defaultMessage: 'Investigate', })} @@ -39,23 +51,12 @@ function ActionMenuButton({ onClick }: { onClick: () => void }) { ); } -export function TransactionActionMenu({ transaction }: Props) { +export function TransactionActionMenu({ transaction, isLoading }: Props) { const license = useLicenseContext(); const hasGoldLicense = license?.isActive && license?.hasAtLeast('gold'); - const { core } = useApmPluginContext(); - const location = useLocation(); - const { urlParams } = useLegacyUrlParams(); - const [isActionPopoverOpen, setIsActionPopoverOpen] = useState(false); - const sections = getSections({ - transaction, - basePath: core.http.basePath, - location, - urlParams, - }); - return ( <> setIsActionPopoverOpen( (prevIsActionPopoverOpen) => !prevIsActionPopoverOpen @@ -73,38 +75,52 @@ export function TransactionActionMenu({ transaction }: Props) { /> } > -
- {sections.map((section, idx) => { - const isLastSection = idx !== sections.length - 1; - return ( -
- {section.map((item) => ( -
- {item.title && {item.title}} - {item.subtitle && ( - {item.subtitle} - )} - - {item.actions.map((action) => ( - - ))} - -
- ))} - {isLastSection && } -
- ); - })} - - {hasGoldLicense && ( - - )} -
+ + {hasGoldLicense && }
); } + +function ActionMenuSections({ transaction }: { transaction?: Transaction }) { + const { core } = useApmPluginContext(); + const location = useLocation(); + const { urlParams } = useLegacyUrlParams(); + + const sections = getSections({ + transaction, + basePath: core.http.basePath, + location, + urlParams, + }); + + return ( +
+ {sections.map((section, idx) => { + const isLastSection = idx !== sections.length - 1; + return ( +
+ {section.map((item) => ( +
+ {item.title && {item.title}} + {item.subtitle && ( + {item.subtitle} + )} + + {item.actions.map((action) => ( + + ))} + +
+ ))} + {isLastSection && } +
+ ); + })} +
+ ); +} From 52f0bf0a6ab8df5d393630571b7c95535723f770 Mon Sep 17 00:00:00 2001 From: Giorgos Bamparopoulos Date: Tue, 29 Mar 2022 09:16:00 +0100 Subject: [PATCH 02/37] Set metadata tab as default and display Stack Trace tab only if stackframes exist (#128598) --- .../waterfall/span_flyout/index.tsx | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/span_flyout/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/span_flyout/index.tsx index 477098aa81d04..5d3c1d733e040 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/span_flyout/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/span_flyout/index.tsx @@ -21,6 +21,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { Fragment } from 'react'; +import { isEmpty } from 'lodash'; import { CompositeSpanDurationSummaryItem } from '../../../../../../shared/summary/composite_span_duration_summary_item'; import { euiStyled } from '../../../../../../../../../../../src/plugins/kibana_react/common'; import { Span } from '../../../../../../../../typings/es_schemas/ui/span'; @@ -216,24 +217,6 @@ export function SpanFlyout({ - - - - ), - }, { id: 'metadata', name: i18n.translate( @@ -249,6 +232,28 @@ export function SpanFlyout({ ), }, + ...(!isEmpty(stackframes) + ? [ + { + id: 'stack-trace', + name: i18n.translate( + 'xpack.apm.transactionDetails.spanFlyout.stackTraceTabLabel', + { + defaultMessage: 'Stack Trace', + } + ), + content: ( + + + + + ), + }, + ] + : []), ]} /> From eb51ea64c5bb531658c38e9995f7455c39822b63 Mon Sep 17 00:00:00 2001 From: Dmitrii Shevchenko Date: Tue, 29 Mar 2022 10:18:33 +0200 Subject: [PATCH 03/37] Add the ability to filter by index pattern to the rules management table (#128245) --- .../detection_rules/sorting.spec.ts | 14 ++-- .../cypress/tasks/alerts_detection_rules.ts | 2 +- .../filters/toggle_selected_group.tsx | 4 +- .../rule_actions_overflow/index.test.tsx | 6 +- .../detection_engine/rules/api.test.ts | 38 ++++++++- .../detection_engine/rules/utils.test.ts | 16 +++- .../detection_engine/rules/utils.ts | 64 +++++++++------ .../detection_engine/rules/all/actions.ts | 19 ----- .../all/bulk_actions/use_bulk_actions.tsx | 23 +++--- .../all/rules_table/rules_table_context.tsx | 27 +++--- .../rules/all/rules_table/use_find_rules.ts | 8 +- .../rules/all/rules_table/utils.ts | 32 +------- .../rules_table_filters.test.tsx | 77 ++++++----------- .../rules_table_filters.tsx | 82 +++++++------------ .../tags_filter_popover.test.tsx | 8 +- .../tags_filter_popover.tsx | 20 +---- .../rules/all/rules_tables.tsx | 62 +++++--------- .../rules/all/utility_bar.tsx | 15 +++- .../detection_engine/rules/translations.ts | 9 +- .../translations/translations/fr-FR.json | 2 - .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - 22 files changed, 225 insertions(+), 307 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/sorting.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/sorting.spec.ts index a162ca4a544d1..7084ff99b5881 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/sorting.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/sorting.spec.ts @@ -13,7 +13,7 @@ import { FOURTH_RULE, RULES_TABLE, pageSelector, - RULES_TABLE_REFRESH_INDICATOR, + RULES_ROW, } from '../../screens/alerts_detection_rules'; import { goToManageAlertsDetectionRules, waitForAlertsPanelToBeLoaded } from '../../tasks/alerts'; @@ -90,14 +90,10 @@ describe('Alerts detection rules', () => { .invoke('text') .then((ruleNameFirstPage) => { goToPage(2); - cy.get(RULES_TABLE_REFRESH_INDICATOR).should('not.exist'); - cy.get(RULES_TABLE) - .find(RULE_NAME) - .first() - .invoke('text') - .should((ruleNameSecondPage) => { - expect(ruleNameFirstPage).not.to.eq(ruleNameSecondPage); - }); + // Check that the rules table shows at least one row + cy.get(RULES_TABLE).find(RULES_ROW).should('have.length.gte', 1); + // Check that the rules table doesn't show the rule from the first page + cy.get(RULES_TABLE).should('not.contain', ruleNameFirstPage); }); cy.get(RULES_TABLE) diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts index 8d125c242be35..afe3981219217 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts @@ -154,7 +154,7 @@ export const goToRuleDetails = () => { }; export const goToTheRuleDetailsOf = (ruleName: string) => { - cy.get(RULE_NAME).contains(ruleName).click(); + cy.get(RULE_NAME).should('contain', ruleName).contains(ruleName).click(); }; export const loadPrebuiltDetectionRules = () => { diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/toggle_selected_group.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/toggle_selected_group.tsx index d452aa9a311e7..4f7064afe642f 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/toggle_selected_group.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/toggle_selected_group.tsx @@ -5,12 +5,10 @@ * 2.0. */ -import { Dispatch, SetStateAction } from 'react'; - export const toggleSelectedGroup = ( group: string, selectedGroups: string[], - setSelectedGroups: Dispatch> + setSelectedGroups: (groups: string[]) => void ): void => { const selectedGroupIndex = selectedGroups.indexOf(group); const updatedSelectedGroups = [...selectedGroups]; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.test.tsx index 3037a3c82f946..71fabef22c904 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.test.tsx @@ -178,7 +178,7 @@ describe('RuleActionsOverflow', () => { ).toEqual(false); }); - test('it calls duplicateRulesAction when rules-details-duplicate-rule is clicked', () => { + test('it calls duplicate action when rules-details-duplicate-rule is clicked', () => { const wrapper = mount( { ); }); - test('it calls duplicateRulesAction with the rule and rule.id when rules-details-duplicate-rule is clicked', () => { + test('it calls duplicate action with the rule and rule.id when rules-details-duplicate-rule is clicked', () => { const rule = mockRule('id'); const wrapper = mount( @@ -210,7 +210,7 @@ describe('RuleActionsOverflow', () => { }); }); - test('it calls editRuleAction after the rule is duplicated', async () => { + test('it navigates to edit page after the rule is duplicated', async () => { const rule = mockRule('id'); const ruleDuplicate = mockRule('newRule'); executeRulesBulkActionMock.mockImplementation(() => diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts index 16613d7cb4d0f..c8d8b5bb6ffd0 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts @@ -142,7 +142,37 @@ describe('Detections Rules API', () => { expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_find', { method: 'GET', query: { - filter: 'alert.attributes.name: hello world', + filter: + '(alert.attributes.name: "hello world" OR alert.attributes.params.index: "hello world" OR alert.attributes.params.threat.tactic.id: "hello world" OR alert.attributes.params.threat.tactic.name: "hello world" OR alert.attributes.params.threat.technique.id: "hello world" OR alert.attributes.params.threat.technique.name: "hello world")', + page: 1, + per_page: 20, + sort_field: 'enabled', + sort_order: 'desc', + }, + signal: abortCtrl.signal, + }); + }); + + test('check parameter url, query with a filter get escaped correctly', async () => { + await fetchRules({ + filterOptions: { + filter: '" OR (foo:bar)', + showCustomRules: false, + showElasticRules: false, + tags: [], + }, + sortingOptions: { + field: 'enabled', + order: 'desc', + }, + signal: abortCtrl.signal, + }); + + expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_find', { + method: 'GET', + query: { + filter: + '(alert.attributes.name: "\\" OR (foo:bar)" OR alert.attributes.params.index: "\\" OR (foo:bar)" OR alert.attributes.params.threat.tactic.id: "\\" OR (foo:bar)" OR alert.attributes.params.threat.tactic.name: "\\" OR (foo:bar)" OR alert.attributes.params.threat.technique.id: "\\" OR (foo:bar)" OR alert.attributes.params.threat.technique.name: "\\" OR (foo:bar)")', page: 1, per_page: 20, sort_field: 'enabled', @@ -226,7 +256,7 @@ describe('Detections Rules API', () => { expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_find', { method: 'GET', query: { - filter: 'alert.attributes.tags: "hello" AND alert.attributes.tags: "world"', + filter: 'alert.attributes.tags:("hello" AND "world")', page: 1, per_page: 20, sort_field: 'enabled', @@ -254,7 +284,7 @@ describe('Detections Rules API', () => { expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_find', { method: 'GET', query: { - filter: 'alert.attributes.tags: "hello" AND alert.attributes.tags: "world"', + filter: 'alert.attributes.tags:("hello" AND "world")', page: 1, per_page: 20, sort_field: 'updatedAt', @@ -353,7 +383,7 @@ describe('Detections Rules API', () => { method: 'GET', query: { filter: - 'alert.attributes.name: ruleName AND alert.attributes.tags: "__internal_immutable:false" AND alert.attributes.tags: "__internal_immutable:true" AND (alert.attributes.tags: "hello" AND alert.attributes.tags: "world")', + 'alert.attributes.tags: "__internal_immutable:false" AND alert.attributes.tags: "__internal_immutable:true" AND alert.attributes.tags:("hello" AND "world") AND (alert.attributes.name: "ruleName" OR alert.attributes.params.index: "ruleName" OR alert.attributes.params.threat.tactic.id: "ruleName" OR alert.attributes.params.threat.tactic.name: "ruleName" OR alert.attributes.params.threat.technique.id: "ruleName" OR alert.attributes.params.threat.technique.name: "ruleName")', page: 1, per_page: 20, sort_field: 'enabled', diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/utils.test.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/utils.test.ts index 7f69d07e83467..e3d2300972a51 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/utils.test.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/utils.test.ts @@ -26,7 +26,17 @@ describe('convertRulesFilterToKQL', () => { it('handles presence of "filter" properly', () => { const kql = convertRulesFilterToKQL({ ...filterOptions, filter: 'foo' }); - expect(kql).toBe('alert.attributes.name: foo'); + expect(kql).toBe( + '(alert.attributes.name: "foo" OR alert.attributes.params.index: "foo" OR alert.attributes.params.threat.tactic.id: "foo" OR alert.attributes.params.threat.tactic.name: "foo" OR alert.attributes.params.threat.technique.id: "foo" OR alert.attributes.params.threat.technique.name: "foo")' + ); + }); + + it('escapes "filter" value properly', () => { + const kql = convertRulesFilterToKQL({ ...filterOptions, filter: '" OR (foo: bar)' }); + + expect(kql).toBe( + '(alert.attributes.name: "\\" OR (foo: bar)" OR alert.attributes.params.index: "\\" OR (foo: bar)" OR alert.attributes.params.threat.tactic.id: "\\" OR (foo: bar)" OR alert.attributes.params.threat.tactic.name: "\\" OR (foo: bar)" OR alert.attributes.params.threat.technique.id: "\\" OR (foo: bar)" OR alert.attributes.params.threat.technique.name: "\\" OR (foo: bar)")' + ); }); it('handles presence of "showCustomRules" properly', () => { @@ -44,7 +54,7 @@ describe('convertRulesFilterToKQL', () => { it('handles presence of "tags" properly', () => { const kql = convertRulesFilterToKQL({ ...filterOptions, tags: ['tag1', 'tag2'] }); - expect(kql).toBe('alert.attributes.tags: "tag1" AND alert.attributes.tags: "tag2"'); + expect(kql).toBe('alert.attributes.tags:("tag1" AND "tag2")'); }); it('handles combination of different properties properly', () => { @@ -56,7 +66,7 @@ describe('convertRulesFilterToKQL', () => { }); expect(kql).toBe( - `alert.attributes.name: foo AND alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:true" AND (alert.attributes.tags: "tag1" AND alert.attributes.tags: "tag2")` + `alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:true" AND alert.attributes.tags:(\"tag1\" AND \"tag2\") AND (alert.attributes.name: \"foo\" OR alert.attributes.params.index: \"foo\" OR alert.attributes.params.threat.tactic.id: \"foo\" OR alert.attributes.params.threat.tactic.name: \"foo\" OR alert.attributes.params.threat.technique.id: \"foo\" OR alert.attributes.params.threat.technique.name: \"foo\")` ); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/utils.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/utils.ts index 841b2adca09e0..f5e52fd6362c1 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/utils.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/utils.ts @@ -6,8 +6,18 @@ */ import { INTERNAL_IMMUTABLE_KEY } from '../../../../../common/constants'; +import { escapeKuery } from '../../../../common/lib/keury'; import { FilterOptions } from './types'; +const SEARCHABLE_RULE_PARAMS = [ + 'alert.attributes.name', + 'alert.attributes.params.index', + 'alert.attributes.params.threat.tactic.id', + 'alert.attributes.params.threat.tactic.name', + 'alert.attributes.params.threat.technique.id', + 'alert.attributes.params.threat.technique.name', +]; + /** * Convert rules filter options object to KQL query * @@ -15,27 +25,35 @@ import { FilterOptions } from './types'; * * @returns KQL string */ -export const convertRulesFilterToKQL = (filterOptions: FilterOptions): string => { - const showCustomRuleFilter = filterOptions.showCustomRules - ? [`alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:false"`] - : []; - const showElasticRuleFilter = filterOptions.showElasticRules - ? [`alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:true"`] - : []; - const filtersWithoutTags = [ - ...(filterOptions.filter.length ? [`alert.attributes.name: ${filterOptions.filter}`] : []), - ...showCustomRuleFilter, - ...showElasticRuleFilter, - ].join(' AND '); - - const tags = filterOptions.tags - .map((t) => `alert.attributes.tags: "${t.replace(/"/g, '\\"')}"`) - .join(' AND '); - - const filterString = - filtersWithoutTags !== '' && tags !== '' - ? `${filtersWithoutTags} AND (${tags})` - : filtersWithoutTags + tags; - - return filterString; +export const convertRulesFilterToKQL = ({ + showCustomRules, + showElasticRules, + filter, + tags, +}: FilterOptions): string => { + const filters: string[] = []; + + if (showCustomRules) { + filters.push(`alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:false"`); + } + + if (showElasticRules) { + filters.push(`alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:true"`); + } + + if (tags.length > 0) { + filters.push( + `alert.attributes.tags:(${tags.map((tag) => `"${escapeKuery(tag)}"`).join(' AND ')})` + ); + } + + if (filter.length) { + const searchQuery = SEARCHABLE_RULE_PARAMS.map( + (param) => `${param}: "${escapeKuery(filter)}"` + ).join(' OR '); + + filters.push(`(${searchQuery})`); + } + + return filters.join(' AND '); }; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/actions.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/actions.ts index 10c099e4bfcc8..488fd3625bf59 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/actions.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/actions.ts @@ -81,25 +81,6 @@ export const executeRulesBulkAction = async ({ } }; -export const initRulesBulkAction = (params: Omit) => { - const byQuery = (query: string) => - executeRulesBulkAction({ - ...params, - search: { query }, - }); - - const byIds = (ids: string[]) => - executeRulesBulkAction({ - ...params, - search: { ids }, - }); - - return { - byQuery, - byIds, - }; -}; - function defaultErrorHandler(toasts: UseAppToasts, action: BulkAction, error: HTTPError) { // if response doesn't have number of failed rules, it means the whole bulk action failed // and general error toast will be shown. Otherwise - error toast for partial failure diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_actions.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_actions.tsx index 491b693a442ba..6bd7cfc67f308 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_actions.tsx @@ -30,7 +30,7 @@ import { canEditRuleWithActions } from '../../../../../../common/utils/privilege import { useRulesTableContext } from '../rules_table/rules_table_context'; import * as detectionI18n from '../../../translations'; import * as i18n from '../../translations'; -import { executeRulesBulkAction, initRulesBulkAction } from '../actions'; +import { executeRulesBulkAction } from '../actions'; import { useHasActionsPrivileges } from '../use_has_actions_privileges'; import { useHasMlPermissions } from '../use_has_ml_permissions'; import { getCustomRulesCountFromCache } from './use_custom_rules_count'; @@ -239,26 +239,23 @@ export const useBulkActions = ({ ); }, 5 * 1000); - const rulesBulkAction = initRulesBulkAction({ + await executeRulesBulkAction({ visibleRuleIds: selectedRuleIds, action: BulkAction.edit, setLoadingRules, toasts, payload: { edit: [editPayload] }, onFinish: () => hideWarningToast(), + search: isAllSelected + ? { + query: convertRulesFilterToKQL({ + ...filterOptions, + showCustomRules: true, // only edit custom rules, as elastic rule are immutable + }), + } + : { ids: customSelectedRuleIds }, }); - // only edit custom rules, as elastic rule are immutable - if (isAllSelected) { - const customRulesOnlyFilterQuery = convertRulesFilterToKQL({ - ...filterOptions, - showCustomRules: true, - }); - await rulesBulkAction.byQuery(customRulesOnlyFilterQuery); - } else { - await rulesBulkAction.byIds(customSelectedRuleIds); - } - isBulkEditFinished = true; invalidateRules(); if (getIsMounted()) { diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table/rules_table_context.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table/rules_table_context.tsx index 2bf20acfb9334..130dd61c371ae 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table/rules_table_context.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table/rules_table_context.tsx @@ -16,7 +16,7 @@ import { SortingOptions, } from '../../../../../containers/detection_engine/rules/types'; import { useFindRules } from './use_find_rules'; -import { getRulesComparator, getRulesPredicate } from './utils'; +import { getRulesComparator } from './utils'; export interface RulesTableState { /** @@ -114,7 +114,7 @@ export interface LoadingRules { export interface RulesTableActions { reFetchRules: ReturnType['refetch']; - setFilterOptions: React.Dispatch>; + setFilterOptions: (newFilter: Partial) => void; setIsAllSelected: React.Dispatch>; setIsInMemorySorting: (value: boolean) => void; setIsRefreshOn: React.Dispatch>; @@ -186,6 +186,13 @@ export const RulesTableContextProvider = ({ const pagination = useMemo(() => ({ page, perPage }), [page, perPage]); + const handleFilterOptionsChange = useCallback((newFilter: Partial) => { + setFilterOptions((currentFilter) => ({ ...currentFilter, ...newFilter })); + setPage(1); + setSelectedRuleIds([]); + setIsAllSelected(false); + }, []); + // Fetch rules const { data: { rules, total } = { rules: [], total: 0 }, @@ -210,15 +217,10 @@ export const RulesTableContextProvider = ({ } }, [isFetched, isRefetching, refetchPrePackagedRulesStatus]); - // Filter rules - const filteredRules = isInMemorySorting ? rules.filter(getRulesPredicate(filterOptions)) : rules; - // Paginate and sort rules const rulesToDisplay = isInMemorySorting - ? filteredRules - .sort(getRulesComparator(sortingOptions)) - .slice((page - 1) * perPage, page * perPage) - : filteredRules; + ? rules.sort(getRulesComparator(sortingOptions)).slice((page - 1) * perPage, page * perPage) + : rules; const providerValue = useMemo( () => ({ @@ -227,7 +229,7 @@ export const RulesTableContextProvider = ({ pagination: { page, perPage, - total: isInMemorySorting ? filteredRules.length : total, + total: isInMemorySorting ? rules.length : total, }, filterOptions, isActionInProgress, @@ -246,7 +248,7 @@ export const RulesTableContextProvider = ({ }, actions: { reFetchRules: refetch, - setFilterOptions, + setFilterOptions: handleFilterOptionsChange, setIsAllSelected, setIsInMemorySorting: toggleInMemorySorting, setIsRefreshOn, @@ -260,7 +262,7 @@ export const RulesTableContextProvider = ({ [ dataUpdatedAt, filterOptions, - filteredRules.length, + handleFilterOptionsChange, isActionInProgress, isAllSelected, isFetched, @@ -274,6 +276,7 @@ export const RulesTableContextProvider = ({ page, perPage, refetch, + rules.length, rulesToDisplay, selectedRuleIds, sortingOptions, diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table/use_find_rules.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table/use_find_rules.ts index 47a2617dd2e25..6544df7e7ed2a 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table/use_find_rules.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table/use_find_rules.ts @@ -31,8 +31,12 @@ export const useFindRules = (args: UseFindRulesArgs) => { // Use this query result when isInMemorySorting = true const allRules = useFindRulesQuery( ['all'], - { pagination: { page: 1, perPage: MAX_RULES_PER_PAGE } }, - { refetchInterval, enabled: isInMemorySorting } + { pagination: { page: 1, perPage: MAX_RULES_PER_PAGE }, filterOptions }, + { + refetchInterval, + enabled: isInMemorySorting, + keepPreviousData: true, // Use this option so that the state doesn't jump between "success" and "loading" on page change + } ); // Use this query result when isInMemorySorting = false diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table/utils.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table/utils.ts index 37deade0d1316..12d114cb3c990 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table/utils.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table/utils.ts @@ -6,11 +6,7 @@ */ import { get } from 'lodash'; -import { - FilterOptions, - Rule, - SortingOptions, -} from '../../../../../containers/detection_engine/rules/types'; +import { Rule, SortingOptions } from '../../../../../containers/detection_engine/rules/types'; /** * Returns a comparator function to be used with .sort() @@ -79,29 +75,3 @@ const compareNumbers = (a: number, b: number, direction: number) => { } return 0; }; - -/** - * Returns a predicate function to be used with .filter() - * - * @param filterOptions Current table filter - */ -export function getRulesPredicate(filterOptions: FilterOptions) { - return (rule: Rule) => { - if ( - filterOptions.filter && - !rule.name.toLowerCase().includes(filterOptions.filter.toLowerCase()) - ) { - return false; - } - if (filterOptions.showCustomRules && rule.immutable) { - return false; - } - if (filterOptions.showElasticRules && !rule.immutable) { - return false; - } - if (filterOptions.tags.length && !filterOptions.tags.every((tag) => rule.tags.includes(tag))) { - return false; - } - return true; - }; -} diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.test.tsx index e627ce3815e59..816ffdfa9dad6 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.test.tsx @@ -7,65 +7,38 @@ import React from 'react'; import { mount } from 'enzyme'; -import { act } from '@testing-library/react'; import { RulesTableFilters } from './rules_table_filters'; -import { useAppToastsMock } from '../../../../../../common/hooks/use_app_toasts.mock'; -import { useAppToasts } from '../../../../../../common/hooks/use_app_toasts'; -jest.mock('../../../../../../common/hooks/use_app_toasts'); +import { TestProviders } from '../../../../../../common/mock'; -describe('RulesTableFilters', () => { - let appToastsMock: jest.Mocked>; - - beforeEach(() => { - jest.resetAllMocks(); - appToastsMock = useAppToastsMock.create(); - (useAppToasts as jest.Mock).mockReturnValue(appToastsMock); - }); +jest.mock('../rules_table/rules_table_context'); +describe('RulesTableFilters', () => { it('renders no numbers next to rule type button filter if none exist', async () => { - await act(async () => { - const wrapper = mount( - ({})} - /> - ); - - expect(wrapper.find('[data-test-subj="showElasticRulesFilterButton"]').at(0).text()).toEqual( - 'Elastic rules' - ); - expect(wrapper.find('[data-test-subj="showCustomRulesFilterButton"]').at(0).text()).toEqual( - 'Custom rules' - ); - }); + const wrapper = mount( + , + { wrappingComponent: TestProviders } + ); + + expect(wrapper.find('[data-test-subj="showElasticRulesFilterButton"]').at(0).text()).toEqual( + 'Elastic rules' + ); + expect(wrapper.find('[data-test-subj="showCustomRulesFilterButton"]').at(0).text()).toEqual( + 'Custom rules' + ); }); it('renders number of custom and prepackaged rules', async () => { - await act(async () => { - const wrapper = mount( - ({})} - /> - ); - - expect(wrapper.find('[data-test-subj="showElasticRulesFilterButton"]').at(0).text()).toEqual( - 'Elastic rules (9)' - ); - expect(wrapper.find('[data-test-subj="showCustomRulesFilterButton"]').at(0).text()).toEqual( - 'Custom rules (10)' - ); - }); + const wrapper = mount( + , + { wrappingComponent: TestProviders } + ); + + expect(wrapper.find('[data-test-subj="showElasticRulesFilterButton"]').at(0).text()).toEqual( + 'Elastic rules (9)' + ); + expect(wrapper.find('[data-test-subj="showCustomRulesFilterButton"]').at(0).text()).toEqual( + 'Custom rules (10)' + ); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.tsx index 5987cd75d303e..b4c81ae5a177d 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.tsx @@ -5,8 +5,6 @@ * 2.0. */ -import React, { useCallback, useEffect, useState } from 'react'; - import { EuiFieldSearch, EuiFilterButton, @@ -15,76 +13,63 @@ import { EuiFlexItem, } from '@elastic/eui'; import { isEqual } from 'lodash/fp'; - +import React, { useCallback } from 'react'; +import styled from 'styled-components'; import * as i18n from '../../translations'; - -import { FilterOptions } from '../../../../../containers/detection_engine/rules'; +import { useRulesTableContext } from '../rules_table/rules_table_context'; import { TagsFilterPopover } from './tags_filter_popover'; +const FilterWrapper = styled(EuiFlexGroup)` + margin-bottom: ${({ theme }) => theme.eui.euiSizeXS}; +`; + interface RulesTableFiltersProps { - onFilterChanged: (filterOptions: Partial) => void; rulesCustomInstalled: number | null; rulesInstalled: number | null; - currentFilterTags: string[]; - tags: string[]; - isLoadingTags: boolean; - reFetchTags: () => void; + allTags: string[]; } /** * Collection of filters for filtering data within the RulesTable. Contains search bar, Elastic/Custom * Rules filter button toggle, and tag selection - * - * @param onFilterChanged change listener to be notified on filter changes */ const RulesTableFiltersComponent = ({ - onFilterChanged, rulesCustomInstalled, rulesInstalled, - currentFilterTags, - tags, - isLoadingTags, - reFetchTags, + allTags, }: RulesTableFiltersProps) => { - const [filter, setFilter] = useState(''); - const [selectedTags, setSelectedTags] = useState([]); - const [showCustomRules, setShowCustomRules] = useState(false); - const [showElasticRules, setShowElasticRules] = useState(false); + const { + state: { filterOptions }, + actions: { setFilterOptions }, + } = useRulesTableContext(); - useEffect(() => { - reFetchTags(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [rulesCustomInstalled, rulesInstalled]); + const { showCustomRules, showElasticRules, tags: selectedTags } = filterOptions; - // Propagate filter changes to parent - useEffect(() => { - onFilterChanged({ filter, showCustomRules, showElasticRules, tags: selectedTags }); - }, [filter, selectedTags, showCustomRules, showElasticRules, onFilterChanged]); - - const handleOnSearch = useCallback((filterString) => setFilter(filterString.trim()), [setFilter]); + const handleOnSearch = useCallback( + (filterString) => setFilterOptions({ filter: filterString.trim() }), + [setFilterOptions] + ); const handleElasticRulesClick = useCallback(() => { - setShowElasticRules(!showElasticRules); - setShowCustomRules(false); - }, [setShowElasticRules, showElasticRules, setShowCustomRules]); + setFilterOptions({ showElasticRules: !showElasticRules, showCustomRules: false }); + }, [setFilterOptions, showElasticRules]); const handleCustomRulesClick = useCallback(() => { - setShowCustomRules(!showCustomRules); - setShowElasticRules(false); - }, [setShowElasticRules, showCustomRules, setShowCustomRules]); + setFilterOptions({ showCustomRules: !showCustomRules, showElasticRules: false }); + }, [setFilterOptions, showCustomRules]); const handleSelectedTags = useCallback( - (newTags) => { + (newTags: string[]) => { if (!isEqual(newTags, selectedTags)) { - setSelectedTags(newTags); + setFilterOptions({ tags: newTags }); } }, - [selectedTags] + [selectedTags, setFilterOptions] ); return ( - - + + - @@ -123,14 +105,12 @@ const RulesTableFiltersComponent = ({ onClick={handleCustomRulesClick} data-test-subj="showCustomRulesFilterButton" > - <> - {i18n.CUSTOM_RULES} - {rulesCustomInstalled != null ? ` (${rulesCustomInstalled})` : ''} - + {i18n.CUSTOM_RULES} + {rulesCustomInstalled != null ? ` (${rulesCustomInstalled})` : ''} - + ); }; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.test.tsx index bb41c2d87cd5a..c8b5e78a94563 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.test.tsx @@ -13,13 +13,7 @@ import { TagsFilterPopover } from './tags_filter_popover'; describe('TagsFilterPopover', () => { it('renders correctly', () => { const wrapper = shallow( - + ); expect(wrapper.find('EuiPopover')).toHaveLength(1); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx index c5262caf6c776..ca2c2b4d00d30 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx @@ -5,15 +5,7 @@ * 2.0. */ -import React, { - ChangeEvent, - Dispatch, - SetStateAction, - useCallback, - useEffect, - useMemo, - useState, -} from 'react'; +import React, { ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react'; import { EuiFilterButton, EuiFilterSelectItem, @@ -33,10 +25,7 @@ import { caseInsensitiveSort } from '../helpers'; interface TagsFilterPopoverProps { selectedTags: string[]; tags: string[]; - onSelectedTagsChanged: Dispatch>; - currentFilterTags: string[]; - // eslint-disable-next-line react/no-unused-prop-types - isLoading: boolean; // TO DO reimplement? + onSelectedTagsChanged: (newTags: string[]) => void; } const PopoverContentWrapper = styled.div` @@ -64,11 +53,10 @@ const TagsFilterPopoverComponent = ({ tags, selectedTags, onSelectedTagsChanged, - currentFilterTags, }: TagsFilterPopoverProps) => { const sortedTags = useMemo( - () => caseInsensitiveSort(Array.from(new Set([...tags, ...currentFilterTags]))), - [tags, currentFilterTags] + () => caseInsensitiveSort(Array.from(new Set([...tags, ...selectedTags]))), + [selectedTags, tags] ); const [isTagPopoverOpen, setIsTagPopoverOpen] = useState(false); const [searchInput, setSearchInput] = useState(''); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx index c38c8e48928f1..3962fa217ccfb 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx @@ -14,19 +14,16 @@ import { EuiLoadingContent, EuiProgress, } from '@elastic/eui'; -import React, { useCallback, useMemo, useRef } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import { partition } from 'lodash/fp'; import { AllRulesTabs } from './rules_table_toolbar'; -import { HeaderSection } from '../../../../../common/components/header_section'; import { Loader } from '../../../../../common/components/loader'; import { useBoolState } from '../../../../../common/hooks/use_bool_state'; import { useValueChanged } from '../../../../../common/hooks/use_value_changed'; -import { useKibana } from '../../../../../common/lib/kibana'; import { PrePackagedRulesPrompt } from '../../../../components/rules/pre_packaged_rules/load_empty_prompt'; import { CreatePreBuiltRules, - FilterOptions, Rule, RulesSortingFields, } from '../../../../containers/detection_engine/rules'; @@ -85,7 +82,6 @@ export const RulesTables = React.memo( rulesNotUpdated, selectedTab, }) => { - const { timelines } = useKibana().services; const tableRef = useRef(null); const rulesTableContext = useRulesTableContext(); @@ -96,11 +92,9 @@ export const RulesTables = React.memo( isActionInProgress, isAllSelected, isFetched, - isFetching, isLoading, isRefetching, isRefreshOn, - lastUpdated, loadingRuleIds, loadingRulesAction, pagination, @@ -109,7 +103,6 @@ export const RulesTables = React.memo( }, actions: { reFetchRules, - setFilterOptions, setIsAllSelected, setIsRefreshOn, setPage, @@ -125,7 +118,12 @@ export const RulesTables = React.memo( rulesNotUpdated ); - const [isLoadingTags, tags, reFetchTags] = useTags(); + const [, allTags, reFetchTags] = useTags(); + + useEffect(() => { + reFetchTags(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [rulesCustomInstalled, rulesInstalled]); const [isDeleteConfirmationVisible, showDeleteConfirmation, hideDeleteConfirmation] = useBoolState(); @@ -183,16 +181,6 @@ export const RulesTables = React.memo( [pagination] ); - const onFilterChangedCallback = useCallback( - (newFilter: Partial) => { - setFilterOptions((currentFilter) => ({ ...currentFilter, ...newFilter })); - setPage(1); - setSelectedRuleIds([]); - setIsAllSelected(false); - }, - [setFilterOptions, setIsAllSelected, setPage, setSelectedRuleIds] - ); - const tableOnChangeCallback = useCallback( ({ page, sort }: EuiBasicTableOnChange) => { setSortingOptions({ @@ -286,9 +274,11 @@ export const RulesTables = React.memo( } : { 'data-test-subj': 'monitoring-table', columns: monitoringColumns }; + const shouldShowLinearProgress = isFetched && isRefetching; + const shouldShowLoadingOverlay = (!isFetched && isRefetching) || isActionInProgress; return ( <> - {isFetched && isRefetching && ( + {shouldShowLinearProgress && ( ( color="accent" /> )} - {((!isFetched && isRefetching) || isActionInProgress) && ( + {shouldShowLoadingOverlay && ( )} - - {shouldShowRulesTable && ( - - )} - + {shouldShowRulesTable && ( + + )} {shouldShowPrepackagedRulesPrompt && ( ( editAction={bulkEditActionType} onClose={handleBulkEditFormCancel} onConfirm={handleBulkEditFormConfirm} - tags={tags} + tags={allTags} /> )} {shouldShowRulesTable && ( diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/utility_bar.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/utility_bar.tsx index 6d9c2f92b214e..5513f70c42297 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/utility_bar.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/utility_bar.tsx @@ -22,6 +22,8 @@ import { UtilityBarText, } from '../../../../../common/components/utility_bar'; import * as i18n from '../translations'; +import { useKibana } from '../../../../../common/lib/kibana'; +import { useRulesTableContextOptional } from './rules_table/rules_table_context'; interface AllRulesUtilityBarProps { canBulkEdit: boolean; @@ -55,6 +57,9 @@ export const AllRulesUtilityBar = React.memo( isBulkActionInProgress, hasDisabledActions, }) => { + const { timelines } = useKibana().services; + const rulesTableContext = useRulesTableContextOptional(); + const handleGetBulkItemsPopoverContent = useCallback( (closePopover: () => void): JSX.Element | null => { if (onGetBulkItemsPopoverContent != null) { @@ -100,7 +105,7 @@ export const AllRulesUtilityBar = React.memo( ); return ( - + {hasBulkActions ? ( @@ -180,6 +185,14 @@ export const AllRulesUtilityBar = React.memo( )} + {rulesTableContext && ( + + {timelines.getLastUpdated({ + showUpdating: rulesTableContext.state.isFetching, + updatedAt: rulesTableContext.state.lastUpdated, + })} + + )} ); } diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts index 55a564f472c59..f93ca2f48d2db 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts @@ -391,13 +391,6 @@ export const EXPORT_FILENAME = i18n.translate( } ); -export const ALL_RULES = i18n.translate( - 'xpack.securitySolution.detectionEngine.rules.allRules.tableTitle', - { - defaultMessage: 'All rules', - } -); - export const SEARCH_RULES = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.allRules.searchAriaLabel', { @@ -408,7 +401,7 @@ export const SEARCH_RULES = i18n.translate( export const SEARCH_PLACEHOLDER = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.allRules.searchPlaceholder', { - defaultMessage: 'e.g. rule name', + defaultMessage: 'Search by rule name, index pattern, or MITRE ATT&CK tactic or technique', } ); diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 7b6e87e2d49db..ddb2ca0490c48 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -20830,14 +20830,12 @@ "xpack.securitySolution.detectionEngine.rules.allRules.inactiveRuleDescription": "inactive", "xpack.securitySolution.detectionEngine.rules.allRules.refreshTitle": "Actualiser", "xpack.securitySolution.detectionEngine.rules.allRules.searchAriaLabel": "Rechercher les règles", - "xpack.securitySolution.detectionEngine.rules.allRules.searchPlaceholder": "par ex. nom de règle", "xpack.securitySolution.detectionEngine.rules.allRules.selectAllRulesTitle": "Sélection totale de {totalRules} {totalRules, plural, =1 {règle} other {règles}} effectuée", "xpack.securitySolution.detectionEngine.rules.allRules.selectedRulesTitle": "Sélection de {selectedRules} {selectedRules, plural, =1 {règle} other {règles}} effectuée", "xpack.securitySolution.detectionEngine.rules.allRules.showingExceptionLists": "Affichage de {totalLists} {totalLists, plural, =1 {liste} other {listes}}", "xpack.securitySolution.detectionEngine.rules.allRules.showingRulesTitle": "Affichage de {totalRules} {totalRules, plural, =1 {règle} other {règles}}", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.successToastDescription": "Duplication réussie de {totalRules, plural, =1 {{totalRules} règle} other {{totalRules} règles}}", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.export.successToastDescription": "Exportation réussie de {exportedRules} sur {totalRules} {totalRules, plural, =1 {règle} other {règles}}. Les règles prédéfinies ont été exclues du fichier résultant.", - "xpack.securitySolution.detectionEngine.rules.allRules.tableTitle": "Toutes les règles", "xpack.securitySolution.detectionEngine.rules.allRules.tabs.exceptions": "Listes d'exceptions", "xpack.securitySolution.detectionEngine.rules.allRules.tabs.monitoring": "Monitoring des règles", "xpack.securitySolution.detectionEngine.rules.allRules.tabs.rules": "Règles", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index e465e82dfa5d5..cc840ab4a3534 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -23836,14 +23836,12 @@ "xpack.securitySolution.detectionEngine.rules.allRules.inactiveRuleDescription": "非アクティブ", "xpack.securitySolution.detectionEngine.rules.allRules.refreshTitle": "更新", "xpack.securitySolution.detectionEngine.rules.allRules.searchAriaLabel": "ルールの検索", - "xpack.securitySolution.detectionEngine.rules.allRules.searchPlaceholder": "例:ルール名", "xpack.securitySolution.detectionEngine.rules.allRules.selectAllRulesTitle": "すべての{totalRules} {totalRules, plural, other {個のルール}}を選択", "xpack.securitySolution.detectionEngine.rules.allRules.selectedRulesTitle": "{selectedRules} {selectedRules, plural, other {ルール}}を選択しました", "xpack.securitySolution.detectionEngine.rules.allRules.showingExceptionLists": "{totalLists} {totalLists, plural, other {件のリスト}}を表示しています。", "xpack.securitySolution.detectionEngine.rules.allRules.showingRulesTitle": "{totalRules} {totalRules, plural, other {ルール}}を表示中", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.successToastDescription": "{totalRules, plural, other {{totalRules}ルール}}を正常に複製しました", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.export.successToastDescription": "{exportedRules}/{totalRules} {totalRules, plural, other {件のルール}}を正常にエクスポートしました事前構築済みルールは結果のファイルから除外されました。", - "xpack.securitySolution.detectionEngine.rules.allRules.tableTitle": "すべてのルール", "xpack.securitySolution.detectionEngine.rules.allRules.tabs.exceptions": "例外リスト", "xpack.securitySolution.detectionEngine.rules.allRules.tabs.monitoring": "ルール監視", "xpack.securitySolution.detectionEngine.rules.allRules.tabs.rules": "ルール", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 737ad8b6e116c..8697109f3b927 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -23863,14 +23863,12 @@ "xpack.securitySolution.detectionEngine.rules.allRules.inactiveRuleDescription": "非活动", "xpack.securitySolution.detectionEngine.rules.allRules.refreshTitle": "刷新", "xpack.securitySolution.detectionEngine.rules.allRules.searchAriaLabel": "搜索规则", - "xpack.securitySolution.detectionEngine.rules.allRules.searchPlaceholder": "例如,规则名", "xpack.securitySolution.detectionEngine.rules.allRules.selectAllRulesTitle": "选择所有 {totalRules} 个{totalRules, plural, other {规则}}", "xpack.securitySolution.detectionEngine.rules.allRules.selectedRulesTitle": "已选择 {selectedRules} 个{selectedRules, plural, other {规则}}", "xpack.securitySolution.detectionEngine.rules.allRules.showingExceptionLists": "正在显示 {totalLists} 个{totalLists, plural, other {列表}}", "xpack.securitySolution.detectionEngine.rules.allRules.showingRulesTitle": "正在显示 {totalRules} 个{totalRules, plural, other {规则}}", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.successToastDescription": "已成功复制 {totalRules, plural, other {{totalRules} 个规则}}", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.export.successToastDescription": "已成功导出 {exportedRules}/{totalRules} 个{totalRules, plural, other {规则}}。预置规则已从结果文件中排除。", - "xpack.securitySolution.detectionEngine.rules.allRules.tableTitle": "所有规则", "xpack.securitySolution.detectionEngine.rules.allRules.tabs.exceptions": "例外列表", "xpack.securitySolution.detectionEngine.rules.allRules.tabs.monitoring": "规则监测", "xpack.securitySolution.detectionEngine.rules.allRules.tabs.rules": "规则", From 617bb305c90bd97683febab280387d0c478d1bb7 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Tue, 29 Mar 2022 10:19:40 +0200 Subject: [PATCH 04/37] [Discover] Cleanup usage of deprecated APIs (#128602) --- src/plugins/discover/public/__mocks__/services.ts | 6 ++---- .../context/services/context_state.test.ts | 3 ++- .../application/main/components/chart/histogram.tsx | 4 ++-- .../main/components/layout/discover_layout.test.tsx | 4 ---- .../__stories__/discover_field_details.stories.tsx | 5 +++-- .../components/sidebar/discover_sidebar.test.tsx | 8 +++----- .../sidebar/discover_sidebar_responsive.test.tsx | 7 +++---- .../public/embeddable/saved_search_embeddable.tsx | 12 ++++-------- .../components/doc_viewer_source/get_height.test.tsx | 3 ++- 9 files changed, 21 insertions(+), 31 deletions(-) diff --git a/src/plugins/discover/public/__mocks__/services.ts b/src/plugins/discover/public/__mocks__/services.ts index bc66ae363711e..5f9511d2a63fe 100644 --- a/src/plugins/discover/public/__mocks__/services.ts +++ b/src/plugins/discover/public/__mocks__/services.ts @@ -21,6 +21,7 @@ import { UI_SETTINGS } from '../../../data/public'; import { TopNavMenu } from '../../../navigation/public'; import { FORMATS_UI_SETTINGS } from 'src/plugins/field_formats/common'; import { LocalStorageMock } from './local_storage_mock'; +import { fieldFormatsMock } from '../../../field_formats/common/mocks'; const dataPlugin = dataPluginMock.createStartContract(); export const discoverServiceMock = { @@ -45,10 +46,7 @@ export const discoverServiceMock = { save: true, }, }, - fieldFormats: { - getDefaultInstance: jest.fn(() => ({ convert: (value: unknown) => value })), - getFormatterForField: jest.fn(() => ({ convert: (value: unknown) => value })), - }, + fieldFormats: fieldFormatsMock, filterManager: dataPlugin.query.filterManager, uiSettings: { get: jest.fn((key: string) => { diff --git a/src/plugins/discover/public/application/context/services/context_state.test.ts b/src/plugins/discover/public/application/context/services/context_state.test.ts index 8f564d56c1042..3eb0e8a1e85d4 100644 --- a/src/plugins/discover/public/application/context/services/context_state.test.ts +++ b/src/plugins/discover/public/application/context/services/context_state.test.ts @@ -6,10 +6,11 @@ * Side Public License, v 1. */ +import { Filter } from '@kbn/es-query'; import { IUiSettingsClient } from 'kibana/public'; import { getState } from './context_state'; import { createBrowserHistory, History } from 'history'; -import { FilterManager, Filter } from '../../../../../data/public'; +import { FilterManager } from '../../../../../data/public'; import { coreMock } from '../../../../../../core/public/mocks'; import { SEARCH_FIELDS_FROM_SOURCE } from '../../../../common'; diff --git a/src/plugins/discover/public/application/main/components/chart/histogram.tsx b/src/plugins/discover/public/application/main/components/chart/histogram.tsx index 369513d3b7a31..22eff35be2325 100644 --- a/src/plugins/discover/public/application/main/components/chart/histogram.tsx +++ b/src/plugins/discover/public/application/main/components/chart/histogram.tsx @@ -65,7 +65,7 @@ export function DiscoverHistogram({ savedSearchData$, timefilterUpdateHandler, }: DiscoverHistogramProps) { - const { data, theme, uiSettings } = useDiscoverServices(); + const { data, theme, uiSettings, fieldFormats } = useDiscoverServices(); const chartTheme = theme.useChartsTheme(); const chartBaseTheme = theme.useChartsBaseTheme(); @@ -207,7 +207,7 @@ export function DiscoverHistogram({ type: TooltipType.VerticalCursor, }; - const xAxisFormatter = data.fieldFormats.deserialize(chartData.yAxisFormat); + const xAxisFormatter = fieldFormats.deserialize(chartData.yAxisFormat); const useLegacyTimeAxis = uiSettings.get(LEGACY_TIME_AXIS, false); diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx index c9a9ea6441a0b..6ff808a65eb5e 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx @@ -43,10 +43,6 @@ function mountComponent(indexPattern: DataView, prevSidebarClosed?: boolean) { const searchSourceMock = createSearchSourceMock({}); const services = { ...discoverServiceMock, - fieldFormats: { - getDefaultInstance: jest.fn(() => ({ convert: (value: unknown) => value })), - getFormatterForField: jest.fn(() => ({ convert: (value: unknown) => value })), - }, storage: new LocalStorageMock({ [SIDEBAR_CLOSED_KEY]: prevSidebarClosed, }) as unknown as Storage, diff --git a/src/plugins/discover/public/application/main/components/sidebar/__stories__/discover_field_details.stories.tsx b/src/plugins/discover/public/application/main/components/sidebar/__stories__/discover_field_details.stories.tsx index 2951840b834b0..3547359209700 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/__stories__/discover_field_details.stories.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/__stories__/discover_field_details.stories.tsx @@ -10,7 +10,8 @@ import { storiesOf } from '@storybook/react'; import React from 'react'; import { KBN_FIELD_TYPES } from '@kbn/field-types'; import { DiscoverFieldDetails } from '../discover_field_details'; -import { DataView, IndexPatternField } from '../../../../../../../data_views/public'; +import { DataViewField } from '../../../../../../../data_views/public'; +import { DataView } from '../../../../../../../data_views/public'; import { fieldSpecMap } from './fields'; import { numericField as field } from './fields'; import { Bucket } from '../types'; @@ -36,7 +37,7 @@ const fieldFormat = { defaultMap, }; -const scriptedField = new IndexPatternField({ +const scriptedField = new DataViewField({ name: 'machine.os', type: 'string', esTypes: ['long'], diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.test.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.test.tsx index 266174a002271..3d3c4b9da4516 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.test.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.test.tsx @@ -31,11 +31,9 @@ import { AvailableFields$ } from '../../utils/use_saved_search'; function getCompProps(): DiscoverSidebarProps { const indexPattern = stubLogstashIndexPattern; - - // @ts-expect-error _.each() is passing additional args to flattenHit - const hits = each(cloneDeep(realHits), indexPattern.flattenHit) as Array< - Record - > as ElasticSearchHit[]; + const hits = each(cloneDeep(realHits), (hit) => + flattenHit(hit, indexPattern) + ) as unknown as ElasticSearchHit[]; const indexPatternList = [ { id: '0', attributes: { title: 'b' } } as SavedObject, diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx index 05e0e25d606f6..ef8fcd145c908 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx @@ -73,10 +73,9 @@ jest.mock('../../utils/calc_field_counts', () => ({ function getCompProps(): DiscoverSidebarResponsiveProps { const indexPattern = stubLogstashIndexPattern; - // @ts-expect-error _.each() is passing additional args to flattenHit - const hits = each(cloneDeep(realHits), (hit) => flattenHit(hit, indexPattern)) as Array< - Record - > as ElasticSearchHit[]; + const hits = each(cloneDeep(realHits), (hit) => + flattenHit(hit, indexPattern) + ) as unknown as ElasticSearchHit[]; const indexPatternList = [ { id: '0', attributes: { title: 'b' } } as SavedObject, diff --git a/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx b/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx index f67adf0976e59..e5a8fb2898e4b 100644 --- a/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx +++ b/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx @@ -7,6 +7,7 @@ */ import { Subscription } from 'rxjs'; +import { onlyDisabledFiltersChanged, Filter } from '@kbn/es-query'; import React from 'react'; import ReactDOM from 'react-dom'; import { i18n } from '@kbn/i18n'; @@ -18,14 +19,9 @@ import { ISearchEmbeddable, SearchInput, SearchOutput } from './types'; import { SavedSearch } from '../services/saved_searches'; import { Adapters, RequestAdapter } from '../../../inspector/common'; import { SEARCH_EMBEDDABLE_TYPE } from './constants'; -import { - APPLY_FILTER_TRIGGER, - esFilters, - FilterManager, - generateFilters, -} from '../../../data/public'; +import { APPLY_FILTER_TRIGGER, FilterManager, generateFilters } from '../../../data/public'; import { DiscoverServices } from '../build_services'; -import { Filter, ISearchSource, Query, TimeRange, FilterStateStore } from '../../../data/public'; +import { ISearchSource, Query, TimeRange, FilterStateStore } from '../../../data/public'; import { DataView, DataViewField } from '../../../data_views/public'; import { SavedSearchEmbeddableComponent } from './saved_search_embeddable_component'; import { UiActionsStart } from '../../../ui_actions/public'; @@ -332,7 +328,7 @@ export class SavedSearchEmbeddable { forceFetch = false }: { forceFetch: boolean } = { forceFetch: false } ) { const isFetchRequired = - !esFilters.onlyDisabledFiltersChanged(this.input.filters, this.prevFilters) || + !onlyDisabledFiltersChanged(this.input.filters, this.prevFilters) || !isEqual(this.prevQuery, this.input.query) || !isEqual(this.prevTimeRange, this.input.timeRange) || !isEqual(searchProps.sort, this.input.sort || this.savedSearch.sort) || diff --git a/src/plugins/discover/public/services/doc_views/components/doc_viewer_source/get_height.test.tsx b/src/plugins/discover/public/services/doc_views/components/doc_viewer_source/get_height.test.tsx index 5b641cced5163..fc8f7498f6efc 100644 --- a/src/plugins/discover/public/services/doc_views/components/doc_viewer_source/get_height.test.tsx +++ b/src/plugins/discover/public/services/doc_views/components/doc_viewer_source/get_height.test.tsx @@ -10,7 +10,8 @@ import { monaco } from '@kbn/monaco'; import { getHeight } from './get_height'; describe('getHeight', () => { - window.innerHeight = 500; + Object.defineProperty(window, 'innerHeight', { writable: true, configurable: true, value: 500 }); + const getMonacoMock = (lineCount: number) => { return { getDomNode: jest.fn(() => { From aa4ce92457ff968903559d2d623302380c326286 Mon Sep 17 00:00:00 2001 From: Aleh Zasypkin Date: Tue, 29 Mar 2022 10:44:00 +0200 Subject: [PATCH 05/37] Update API keys docs to remove unnecessary API keys service section. (#128464) --- docs/user/security/api-keys/index.asciidoc | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/docs/user/security/api-keys/index.asciidoc b/docs/user/security/api-keys/index.asciidoc index 94301568b6438..bc277609d43e4 100644 --- a/docs/user/security/api-keys/index.asciidoc +++ b/docs/user/security/api-keys/index.asciidoc @@ -19,20 +19,6 @@ To manage API keys, open the main menu, then click *Stack Management > API Keys* [role="screenshot"] image:images/api-keys.png["API Keys UI"] -[float] -[[api-keys-service]] -=== {es} API key service - -The {es} API key service is automatically enabled when you configure -{ref}/configuring-tls.html#tls-http[TLS on the HTTP interface]. -This ensures that clients are unable to send API keys in clear-text. - -When HTTPS connections are not enabled between {kib} and {es}, -you cannot create or manage API keys, and you get an error message. -For more information, see the -{ref}/security-api-create-api-key.html[{es} API key documentation], -or contact your system administrator. - [float] [[api-keys-security-privileges]] === Security privileges From bef90a58663b6c4b668a7fe0ce45a002fb68c474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yulia=20=C4=8Cech?= <6585477+yuliacech@users.noreply.github.com> Date: Tue, 29 Mar 2022 10:44:11 +0200 Subject: [PATCH 06/37] Jest axe a11y testing (#127185) * [A11y] Added axe testing to testBed to be used in jest tests (CITs) * [A11y] Refactored axe helpers to be outside of testBed for better separation of concerns. Also added tests for indices tab in Index Management. * [A11y] Switched `axe_helpers` to use a Kibana wide axe config and rules that are now in kbn-test package * [A11y] Switched `axe_helpers` to use a Kibana wide axe config and rules that are now in kbn-test package * [A11y] Completed a11y tests in Index Management Indices tab. Also refactored http requests helper file. * Removed import comment * Reverted yarn.lock change * Correct yarn.lock changes * [IM] Updated the branch after merging main * [IM] Reverted unrelated ResponseError interface changes * [IM] Fixed eslint issue with kbn/test import --- package.json | 2 + packages/kbn-test-jest-helpers/BUILD.bazel | 5 + .../kbn-test-jest-helpers/src/axe_helpers.ts | 35 +++++ packages/kbn-test-jest-helpers/src/index.ts | 2 + .../src/testbed/types.ts | 2 +- packages/kbn-test/BUILD.bazel | 1 + .../kbn-test/src/a11y/config.ts | 0 packages/kbn-test/src/index.ts | 2 + test/accessibility/services/a11y/a11y.ts | 2 +- .../apm/ftr_e2e/cypress/support/commands.ts | 5 +- .../applications/shared/cypress/commands.ts | 6 +- .../__jest__/a11y/indices_tab.a11y.test.ts | 125 ++++++++++++++++++ .../client_integration/helpers/fixtures.ts | 47 +++++++ .../helpers/http_requests.ts | 17 ++- .../client_integration/helpers/index.ts | 2 + .../helpers/test_subjects.ts | 3 +- yarn.lock | 85 ++++++++++-- 17 files changed, 317 insertions(+), 24 deletions(-) create mode 100644 packages/kbn-test-jest-helpers/src/axe_helpers.ts rename test/accessibility/services/a11y/constants.ts => packages/kbn-test/src/a11y/config.ts (100%) create mode 100644 x-pack/plugins/index_management/__jest__/a11y/indices_tab.a11y.test.ts create mode 100644 x-pack/plugins/index_management/__jest__/client_integration/helpers/fixtures.ts diff --git a/package.json b/package.json index b8ec92ab796d6..4eba243e30b8a 100644 --- a/package.json +++ b/package.json @@ -571,6 +571,7 @@ "@types/inquirer": "^7.3.1", "@types/intl-relativeformat": "^2.1.0", "@types/jest": "^26.0.22", + "@types/jest-axe": "^3.5.3", "@types/jest-specific-snapshot": "^0.5.5", "@types/joi": "^17.2.3", "@types/jquery": "^3.3.31", @@ -821,6 +822,7 @@ "is-glob": "^4.0.1", "is-path-inside": "^3.0.2", "jest": "^26.6.3", + "jest-axe": "^5.0.0", "jest-canvas-mock": "^2.3.1", "jest-circus": "^26.6.3", "jest-cli": "^26.6.3", diff --git a/packages/kbn-test-jest-helpers/BUILD.bazel b/packages/kbn-test-jest-helpers/BUILD.bazel index d910fab5295d5..c97859e8baab1 100644 --- a/packages/kbn-test-jest-helpers/BUILD.bazel +++ b/packages/kbn-test-jest-helpers/BUILD.bazel @@ -34,6 +34,7 @@ NPM_MODULE_EXTRA_FILES = [ RUNTIME_DEPS = [ "//packages/kbn-dev-utils", "//packages/kbn-i18n-react", + "//packages/kbn-test", "//packages/kbn-std", "//packages/kbn-utils", "@npm//@elastic/elasticsearch", @@ -51,6 +52,7 @@ RUNTIME_DEPS = [ "@npm//he", "@npm//history", "@npm//jest", + "@npm//jest-axe", "@npm//jest-cli", "@npm//jest-snapshot", "@npm//jest-styled-components", @@ -76,9 +78,11 @@ TYPES_DEPS = [ "//packages/kbn-dev-utils:npm_module_types", "//packages/kbn-i18n-react:npm_module_types", "//packages/kbn-std:npm_module_types", + "//packages/kbn-test:npm_module_types", "//packages/kbn-utils:npm_module_types", "@npm//@elastic/elasticsearch", "@npm//axios", + "@npm//axe-core", "@npm//elastic-apm-node", "@npm//del", "@npm//exit-hook", @@ -96,6 +100,7 @@ TYPES_DEPS = [ "@npm//@types/he", "@npm//@types/history", "@npm//@types/jest", + "@npm//@types/jest-axe", "@npm//@types/joi", "@npm//@types/lodash", "@npm//@types/mustache", diff --git a/packages/kbn-test-jest-helpers/src/axe_helpers.ts b/packages/kbn-test-jest-helpers/src/axe_helpers.ts new file mode 100644 index 0000000000000..215209546f956 --- /dev/null +++ b/packages/kbn-test-jest-helpers/src/axe_helpers.ts @@ -0,0 +1,35 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { configureAxe } from 'jest-axe'; +import { Result } from 'axe-core'; +import { AXE_OPTIONS, AXE_CONFIG } from '@kbn/test'; +import { ReactWrapper } from './testbed/types'; + +const axeRunner = configureAxe({ globalOptions: { ...AXE_CONFIG } }); + +/** + * Function to test if a component doesn't have a11y violations from axe automated testing + * @param component + */ +export const expectToBeAccessible = async (component: ReactWrapper): Promise => { + const violations = await getA11yViolations(component); + expect(violations).toHaveLength(0); +}; + +/** + * Returns a11y violations as found by axe testing + * @param component + */ +export const getA11yViolations = async (component: ReactWrapper): Promise => { + const axeResults = await axeRunner(component.html(), { + ...AXE_OPTIONS, + resultTypes: ['violations'], + }); + return axeResults.violations; +}; diff --git a/packages/kbn-test-jest-helpers/src/index.ts b/packages/kbn-test-jest-helpers/src/index.ts index 3594df854cbe4..809d4380df10a 100644 --- a/packages/kbn-test-jest-helpers/src/index.ts +++ b/packages/kbn-test-jest-helpers/src/index.ts @@ -24,6 +24,8 @@ export * from './stub_web_worker'; export * from './testbed'; +export * from './axe_helpers'; + export const nextTick = () => new Promise((res) => process.nextTick(res)); export const delay = (time = 0) => new Promise((resolve) => setTimeout(resolve, time)); diff --git a/packages/kbn-test-jest-helpers/src/testbed/types.ts b/packages/kbn-test-jest-helpers/src/testbed/types.ts index 11f8c802a9751..15996646ec80a 100644 --- a/packages/kbn-test-jest-helpers/src/testbed/types.ts +++ b/packages/kbn-test-jest-helpers/src/testbed/types.ts @@ -30,7 +30,7 @@ export interface EuiTableMetaData { } export interface TestBed { - /** The comonent under test */ + /** The component under test */ component: ReactWrapper; /** * Pass it a `data-test-subj` and it will return true if it exists or false if it does not exist. diff --git a/packages/kbn-test/BUILD.bazel b/packages/kbn-test/BUILD.bazel index 4dc8d684941f3..99d8c64c88032 100644 --- a/packages/kbn-test/BUILD.bazel +++ b/packages/kbn-test/BUILD.bazel @@ -86,6 +86,7 @@ TYPES_DEPS = [ "@npm//@elastic/elasticsearch", "@npm//@jest/console", "@npm//@jest/reporters", + "@npm//axe-core", "@npm//axios", "@npm//elastic-apm-node", "@npm//del", diff --git a/test/accessibility/services/a11y/constants.ts b/packages/kbn-test/src/a11y/config.ts similarity index 100% rename from test/accessibility/services/a11y/constants.ts rename to packages/kbn-test/src/a11y/config.ts diff --git a/packages/kbn-test/src/index.ts b/packages/kbn-test/src/index.ts index c9f0e67c558f1..a0e45f9d7b752 100644 --- a/packages/kbn-test/src/index.ts +++ b/packages/kbn-test/src/index.ts @@ -69,3 +69,5 @@ export { runJest } from './jest/run'; export * from './kbn_archiver_cli'; export * from './kbn_client'; + +export { AXE_CONFIG, AXE_OPTIONS } from './a11y/config'; diff --git a/test/accessibility/services/a11y/a11y.ts b/test/accessibility/services/a11y/a11y.ts index f4d5ceba5a6e3..fd4362c1c82b4 100644 --- a/test/accessibility/services/a11y/a11y.ts +++ b/test/accessibility/services/a11y/a11y.ts @@ -8,9 +8,9 @@ import chalk from 'chalk'; import testSubjectToCss from '@kbn/test-subj-selector'; +import { AXE_CONFIG, AXE_OPTIONS } from '@kbn/test'; import { FtrService } from '../../ftr_provider_context'; -import { AXE_CONFIG, AXE_OPTIONS } from './constants'; import { AxeReport, printResult } from './axe_report'; // @ts-ignore JS that is run in browser as is import { analyzeWithAxe, analyzeWithAxeWithClient } from './analyze_with_axe'; diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts b/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts index 91edae9046f6d..89d8fa620c183 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts @@ -7,10 +7,7 @@ import 'cypress-real-events/support'; import { Interception } from 'cypress/types/net-stubbing'; import 'cypress-axe'; -import { - AXE_CONFIG, - AXE_OPTIONS, -} from 'test/accessibility/services/a11y/constants'; +import { AXE_CONFIG, AXE_OPTIONS } from '@kbn/test'; Cypress.Commands.add('loginAsReadOnlyUser', () => { cy.loginAs({ username: 'apm_read_user', password: 'changeme' }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/cypress/commands.ts b/x-pack/plugins/enterprise_search/public/applications/shared/cypress/commands.ts index 475343948f348..083d55e6f7029 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/cypress/commands.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/cypress/commands.ts @@ -38,9 +38,11 @@ export const login = ({ * Cypress setup/helpers */ +// eslint complains this should be in `dependencies` and not `devDependencies`, but these tests should only run on dev // eslint-disable-next-line import/no-extraneous-dependencies -import 'cypress-axe'; // eslint complains this should be in `dependencies` and not `devDependencies`, but these tests should only run on dev -import { AXE_CONFIG, AXE_OPTIONS } from 'test/accessibility/services/a11y/constants'; +import 'cypress-axe'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { AXE_CONFIG, AXE_OPTIONS } from '@kbn/test'; const axeConfig = { ...AXE_CONFIG, diff --git a/x-pack/plugins/index_management/__jest__/a11y/indices_tab.a11y.test.ts b/x-pack/plugins/index_management/__jest__/a11y/indices_tab.a11y.test.ts new file mode 100644 index 0000000000000..dada1c0fc91c5 --- /dev/null +++ b/x-pack/plugins/index_management/__jest__/a11y/indices_tab.a11y.test.ts @@ -0,0 +1,125 @@ +/* + * 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 { act } from 'react-dom/test-utils'; +// import { expectToBeAccessible } from '@kbn/test-jest-helpers'; +import { getA11yViolations } from '@kbn/test-jest-helpers'; +import { IndicesTestBed, setup } from '../client_integration/home/indices_tab.helpers'; +import { + indexMappings, + indexSettings, + indexStats, + setupEnvironment, +} from '../client_integration/helpers'; +import { + createDataStreamBackingIndex, + createNonDataStreamIndex, +} from '../client_integration/home/data_streams_tab.helpers'; + +describe('A11y Indices tab', () => { + let testBed: IndicesTestBed; + let httpSetup: ReturnType['httpSetup']; + let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; + + beforeEach(() => { + const mockEnvironment = setupEnvironment(); + httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; + httpSetup = mockEnvironment.httpSetup; + }); + + it('when there are no indices', async () => { + httpRequestsMockHelpers.setLoadIndicesResponse([]); + await act(async () => { + testBed = await setup(httpSetup); + }); + const { component } = testBed; + component.update(); + // this is expected to fail and needs to be updated when EUI 52.0.0 is available in Kibana + // await expectToBeAccessible(component); + // until then check that only 1 expected violation is found + const violations = await getA11yViolations(component); + expect(violations).toHaveLength(1); + const { id } = violations[0]; + expect(id).toEqual('aria-allowed-attr'); + }); + + it('when there are indices', async () => { + httpRequestsMockHelpers.setLoadIndicesResponse([ + createNonDataStreamIndex('non-data-stream-test-index'), + createDataStreamBackingIndex('data-stream-test-index', 'test-data-stream'), + ]); + await act(async () => { + testBed = await setup(httpSetup); + }); + const { component } = testBed; + component.update(); + // this is expected to fail and needs to be updated when EUI 52.0.0 is available in Kibana + // await expectToBeAccessible(component); + // until then check that only 1 expected violation is found + const violations = await getA11yViolations(component); + expect(violations).toHaveLength(1); + const { id } = violations[0]; + expect(id).toEqual('aria-allowed-attr'); + }); + + describe('index details flyout', () => { + beforeEach(async () => { + httpRequestsMockHelpers.setLoadIndicesResponse([ + createNonDataStreamIndex('non-data-stream-test-index'), + ]); + httpRequestsMockHelpers.setLoadIndexSettingsResponse(indexSettings); + httpRequestsMockHelpers.setLoadIndexMappingResponse(indexMappings); + httpRequestsMockHelpers.setLoadIndexStatsResponse(indexStats); + await act(async () => { + testBed = await setup(httpSetup); + }); + const { component, find } = testBed; + component.update(); + find('indexTableIndexNameLink').at(0).simulate('click'); + component.update(); + }); + + it('summary tab', async () => { + const { component, find } = testBed; + expect(find('detailPanelTabSelected').text()).toEqual('Summary'); + // this is expected to fail and needs to be updated when EUI 52.0.0 is available in Kibana + // await expectToBeAccessible(component); + // until then check that only 1 expected violation is found + const violations = await getA11yViolations(component); + expect(violations).toHaveLength(1); + const { id } = violations[0]; + expect(id).toEqual('aria-allowed-attr'); + }); + ['settings', 'mappings', 'stats'].forEach((tab) => { + it(`${tab} tab`, async () => { + const { component, find, actions } = testBed; + await actions.selectIndexDetailsTab(tab as 'settings'); + expect(find('detailPanelTabSelected').text().toLowerCase()).toEqual(tab); + // this is expected to fail and needs to be updated when EUI 52.0.0 is available in Kibana + // await expectToBeAccessible(component); + // until then check that only 1 expected violation is found + const violations = await getA11yViolations(component); + expect(violations).toHaveLength(1); + const { id } = violations[0]; + expect(id).toEqual('aria-allowed-attr'); + }); + }); + + it('edit settings tab', async () => { + const { component, find, actions } = testBed; + await actions.selectIndexDetailsTab('edit_settings'); + expect(find('detailPanelTabSelected').text()).toEqual('Edit settings'); + // this is expected to fail and needs to be updated when EUI 52.0.0 is available in Kibana + // await expectToBeAccessible(component); + // until then check that only 1 expected violation is found + const violations = await getA11yViolations(component); + expect(violations).toHaveLength(1); + const { id } = violations[0]; + expect(id).toEqual('aria-allowed-attr'); + }); + }); +}); diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/fixtures.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/fixtures.ts new file mode 100644 index 0000000000000..9f2423b1056f8 --- /dev/null +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/fixtures.ts @@ -0,0 +1,47 @@ +/* + * 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 indexSettings = { + settings: { index: { number_of_shards: '1' } }, + defaults: { index: { flush_after_merge: '512mb' } }, +}; + +export const indexMappings = { + mappings: { + dynamic: 'strict', + properties: { + '@timestamp': { + type: 'date', + }, + }, + }, +}; + +export const indexStats = { + _shards: { + total: 1, + successful: 1, + failed: 0, + }, + stats: { + uuid: 'test-uuid', + health: 'green', + status: 'open', + primaries: { + docs: { + count: 0, + deleted: 0, + }, + }, + total: { + docs: { + count: 0, + deleted: 0, + }, + }, + }, +}; diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts index 4726286319e52..2be3d9c9460cf 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts @@ -94,11 +94,14 @@ const registerHttpRequestMockHelpers = ( const setCreateTemplateResponse = (response?: HttpResponse, error?: ResponseError) => mockResponse('POST', `${API_BASE_PATH}/index_templates`, response, error); - const setUpdateTemplateResponse = ( - templateId: string, - response?: HttpResponse, - error?: ResponseError - ) => mockResponse('PUT', `${API_BASE_PATH}/index_templates/${templateId}`, response, error); + const setLoadIndexSettingsResponse = (response?: HttpResponse, error?: ResponseError) => + mockResponse('GET', `${API_BASE_PATH}/settings/:name`, response, error); + + const setLoadIndexMappingResponse = (response?: HttpResponse, error?: ResponseError) => + mockResponse('GET', `${API_BASE_PATH}/mapping/:name`, response, error); + + const setLoadIndexStatsResponse = (response?: HttpResponse, error?: ResponseError) => + mockResponse('GET', `${API_BASE_PATH}/stats/:name`, response, error); const setUpdateIndexSettingsResponse = ( indexName: string, @@ -128,7 +131,9 @@ const registerHttpRequestMockHelpers = ( setDeleteTemplateResponse, setLoadTemplateResponse, setCreateTemplateResponse, - setUpdateTemplateResponse, + setLoadIndexSettingsResponse, + setLoadIndexMappingResponse, + setLoadIndexStatsResponse, setUpdateIndexSettingsResponse, setSimulateTemplateResponse, setLoadComponentTemplatesResponse, diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/index.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/index.ts index ba2dea966d8cb..1fad428342482 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/index.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/index.ts @@ -18,3 +18,5 @@ export { } from './setup_environment'; export type { TestSubjects } from './test_subjects'; + +export * from './fixtures'; diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/test_subjects.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/test_subjects.ts index f7066fbeda95e..2574594c7fcf8 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/test_subjects.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/test_subjects.ts @@ -62,4 +62,5 @@ export type TestSubjects = | 'unfreezeIndexMenuButton' | 'updateEditIndexSettingsButton' | 'updateIndexSettingsErrorCallout' - | 'viewButton'; + | 'viewButton' + | 'detailPanelTabSelected'; diff --git a/yarn.lock b/yarn.lock index 6814b5463143e..5180f8fbbc6b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5826,6 +5826,14 @@ dependencies: "@types/istanbul-lib-report" "*" +"@types/jest-axe@^3.5.3": + version "3.5.3" + resolved "https://registry.yarnpkg.com/@types/jest-axe/-/jest-axe-3.5.3.tgz#5af918553388aa0a448af75603b44093985778c6" + integrity sha512-ad9qI9f+00N8IlOuGh6dnZ6o0BDdV9VhGfTUr1zCejsPvOfZd6eohffe4JYxUoUuRYEftyMcaJ6Ux4+MsOpGHg== + dependencies: + "@types/jest" "*" + axe-core "^3.5.5" + "@types/jest-specific-snapshot@^0.5.3", "@types/jest-specific-snapshot@^0.5.5": version "0.5.5" resolved "https://registry.yarnpkg.com/@types/jest-specific-snapshot/-/jest-specific-snapshot-0.5.5.tgz#47ce738870be99898ed6d7b08dbf0240c74ae553" @@ -8447,6 +8455,16 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== +axe-core@4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.2.1.tgz#2e50bcf10ee5b819014f6e342e41e45096239e34" + integrity sha512-evY7DN8qSIbsW2H/TWQ1bX3sXN1d4MNb5Vb4n7BzPuCwRHdkZ1H2eNLuSh73EoQqkGKUtju2G2HCcjCfhvZIAA== + +axe-core@^3.5.5: + version "3.5.6" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-3.5.6.tgz#e762a90d7f6dbd244ceacb4e72760ff8aad521b5" + integrity sha512-LEUDjgmdJoA3LqklSTwKYqkjcZ4HKc4ddIYGSAiSkr46NTjzg2L9RNB+lekO9P7Dlpa87+hBtzc2Fzn/+GUWMQ== + axe-core@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.0.2.tgz#c7cf7378378a51fcd272d3c09668002a4990b1cb" @@ -9752,6 +9770,14 @@ chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4. escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chalk@4.1.0, chalk@^4.0.0, chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@4.1.2, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -9779,14 +9805,6 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0, chalk@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" - integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - chalk@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.5.1.tgz#663b3a648b68b55d04690d49167aa837858f2174" @@ -12376,6 +12394,11 @@ diff-sequences@^27.0.6: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.0.6.tgz#3305cb2e55a033924054695cc66019fd7f8e5723" integrity sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ== +diff-sequences@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" + integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== + diff@5.0.0, diff@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" @@ -17573,6 +17596,16 @@ jake@^10.6.1: filelist "^1.0.1" minimatch "^3.0.4" +jest-axe@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/jest-axe/-/jest-axe-5.0.1.tgz#26c43643b2e5f2bd4900c1ab36f8283635957a6e" + integrity sha512-MMOWA6gT4pcZGbTLS8ZEqABH08Lnj5bInfLPpn9ADWX2wFF++odbbh8csmSfkwKjHaioVPzlCtIypAtxFDx/rw== + dependencies: + axe-core "4.2.1" + chalk "4.1.0" + jest-matcher-utils "27.0.2" + lodash.merge "4.6.2" + jest-canvas-mock@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/jest-canvas-mock/-/jest-canvas-mock-2.3.1.tgz#9535d14bc18ccf1493be36ac37dd349928387826" @@ -17680,6 +17713,16 @@ jest-diff@^26.0.0, jest-diff@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" +jest-diff@^27.0.2: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" + integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== + dependencies: + chalk "^4.0.0" + diff-sequences "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + jest-diff@^27.2.0: version "27.2.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.2.0.tgz#bda761c360f751bab1e7a2fe2fc2b0a35ce8518c" @@ -17752,6 +17795,11 @@ jest-get-type@^26.3.0: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== +jest-get-type@^27.0.1, jest-get-type@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" + integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== + jest-get-type@^27.0.6: version "27.0.6" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.0.6.tgz#0eb5c7f755854279ce9b68a9f1a4122f69047cfe" @@ -17810,6 +17858,16 @@ jest-leak-detector@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" +jest-matcher-utils@27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.0.2.tgz#f14c060605a95a466cdc759acc546c6f4cbfc4f0" + integrity sha512-Qczi5xnTNjkhcIB0Yy75Txt+Ez51xdhOxsukN7awzq2auZQGPHcQrJ623PZj0ECDEMOk2soxWx05EXdXGd1CbA== + dependencies: + chalk "^4.0.0" + jest-diff "^27.0.2" + jest-get-type "^27.0.1" + pretty-format "^27.0.2" + jest-matcher-utils@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz#f5b3661d5e628dffe6dd65251dfdae0e87c3a073" @@ -19162,7 +19220,7 @@ lodash.memoize@~3.0.3: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f" integrity sha1-LcvSwofLwKVcxCMovQxzYVDVPj8= -lodash.merge@^4.6.2: +lodash.merge@4.6.2, lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== @@ -22889,6 +22947,15 @@ pretty-format@^26.0.0, pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" +pretty-format@^27.0.2, pretty-format@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + pretty-format@^27.2.0: version "27.2.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.2.0.tgz#ee37a94ce2a79765791a8649ae374d468c18ef19" From 2dcdbbe658e12a713280c7fcb8a8956a672fde90 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 29 Mar 2022 11:58:26 +0300 Subject: [PATCH 07/37] [XY] `system_palette` and `palette` support. (#128053) * Added support of `system_palette` and `palette` types at the `dataLayer` `palette` arg. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Marta Bondyra --- .../expression_functions/data_layer_config.ts | 4 +- .../__snapshots__/to_expression.test.ts.snap | 16 +++++++ .../public/xy_visualization/to_expression.ts | 46 ++++++++++--------- 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts index 3aac992d674d9..e9ffa0f41f49f 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts @@ -106,11 +106,11 @@ export const dataLayerConfigFunction: ExpressionFunctionDefinition< }), }, palette: { - default: `{theme "palette" default={system_palette name="default"} }`, + types: ['palette', 'system_palette'], help: i18n.translate('expressionXY.dataLayer.palette.help', { defaultMessage: 'Palette', }), - types: ['palette'], + default: '{palette}', }, }, fn(input, args) { diff --git a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap index 27cc383834049..256df38ffa5b3 100644 --- a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap +++ b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap @@ -113,6 +113,22 @@ Object { "layerId": Array [ "first", ], + "palette": Array [ + Object { + "chain": Array [ + Object { + "arguments": Object { + "name": Array [ + "default", + ], + }, + "function": "system_palette", + "type": "function", + }, + ], + "type": "expression", + }, + ], "seriesType": Array [ "area", ], diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index ec9093a999c84..86ae7e0bc328e 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -480,29 +480,31 @@ const dataLayerToExpression = ( seriesType: [layer.seriesType], accessors: layer.accessors, columnToLabel: [JSON.stringify(columnToLabel)], - ...(layer.palette - ? { - palette: [ - { - type: 'expression', - chain: [ - { - type: 'function', - function: 'theme', - arguments: { - variable: ['palette'], - default: [ - paletteService - .get(layer.palette.name) - .toExpression(layer.palette.params), - ], - }, + palette: [ + { + type: 'expression', + chain: [ + layer.palette + ? { + type: 'function', + function: 'theme', + arguments: { + variable: ['palette'], + default: [ + paletteService.get(layer.palette.name).toExpression(layer.palette.params), + ], }, - ], - }, - ], - } - : {}), + } + : { + type: 'function', + function: 'system_palette', + arguments: { + name: ['default'], + }, + }, + ], + }, + ], }, }, ], From 7cb72014aab9d5bb403c1b3507922c44c18acefa Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Tue, 29 Mar 2022 11:08:21 +0200 Subject: [PATCH 08/37] Bump @elastic/request-crypto from v2.0.0 to v2.0.1 (#128710) --- package.json | 3 +-- yarn.lock | 69 ++++++++++++++++++++++++++++++++++------------------ 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index 4eba243e30b8a..35edc14f01325 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,6 @@ "**/json-schema": "^0.4.0", "**/minimatch": "^3.1.2", "**/minimist": "^1.2.6", - "**/node-forge": "^1.3.0", "**/pdfkit/crypto-js": "4.0.0", "**/react-syntax-highlighter": "^15.3.1", "**/react-syntax-highlighter/**/highlight.js": "^10.4.1", @@ -114,7 +113,7 @@ "@elastic/node-crypto": "1.2.1", "@elastic/numeral": "^2.5.1", "@elastic/react-search-ui": "^1.6.0", - "@elastic/request-crypto": "2.0.0", + "@elastic/request-crypto": "2.0.1", "@elastic/safer-lodash-set": "link:bazel-bin/packages/elastic-safer-lodash-set", "@elastic/search-ui-app-search-connector": "^1.6.0", "@emotion/cache": "^11.4.0", diff --git a/yarn.lock b/yarn.lock index 5180f8fbbc6b8..ce7e815915a8b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1613,14 +1613,14 @@ "@elastic/react-search-ui-views" "1.6.0" "@elastic/search-ui" "1.6.0" -"@elastic/request-crypto@2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@elastic/request-crypto/-/request-crypto-2.0.0.tgz#a3988be98ed63398c3d0177948c7ac2a857835bb" - integrity sha512-jIReBeHzgUKNsYwdhnTyLFSGHyLsJgQIIw+AMEnq1lcONA/uKG7hhFUK2Z8XxjJ86/oXt1TwJ4BIagZR3mzq7w== +"@elastic/request-crypto@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@elastic/request-crypto/-/request-crypto-2.0.1.tgz#88dc41e4bbba6764c323e1a820757607e2f4f720" + integrity sha512-ZFZ+CF1hb22+BLAe93D4Kc1EMPfVDKckd8SY6IHk/N4H1WUthQ9xXfPVx06r+pTkz62HliyQfXgLKFTQa+aSmw== dependencies: "@elastic/node-crypto" "1.1.1" - "@types/node-jose" "1.1.8" - node-jose "2.0.0" + "@types/node-jose" "1.1.10" + node-jose "2.1.0" "@elastic/safer-lodash-set@link:bazel-bin/packages/elastic-safer-lodash-set": version "0.0.0" @@ -6364,10 +6364,10 @@ dependencies: "@types/node" "*" -"@types/node-jose@1.1.8": - version "1.1.8" - resolved "https://registry.yarnpkg.com/@types/node-jose/-/node-jose-1.1.8.tgz#26b7ae96e3309acb426ea5393ff5d6a0c1c38625" - integrity sha512-AFcArbplUaO+DqGVEPaiz/guw3uUA+dRHjaj26EEDF0DmTEPUd3dEdfdJMUx4kD65EAR3TnI1iHIcb31+Ko87Q== +"@types/node-jose@1.1.10": + version "1.1.10" + resolved "https://registry.yarnpkg.com/@types/node-jose/-/node-jose-1.1.10.tgz#1fc559b63e665f27acedbcb91601e2fee256fad0" + integrity sha512-7L0ucJTugW4x/sYpQ+c5IudAwr0pFuxDVnZLpHKWpff7p1lVa3wTuNvnrzFBNeLojE+UY0cVCwNGXLxXsMIrzw== dependencies: "@types/node" "*" @@ -9432,6 +9432,14 @@ buffer@^5.2.1: base64-js "^1.3.1" ieee754 "^1.1.13" +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + buffer@~5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6" @@ -16401,7 +16409,7 @@ ieee754@^1.1.12, ieee754@^1.1.4: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== -ieee754@^1.1.13: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -19380,6 +19388,11 @@ long@^4.0.0: resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== +long@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/long/-/long-5.2.0.tgz#2696dadf4b4da2ce3f6f6b89186085d94d52fd61" + integrity sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w== + longest-streak@^2.0.0, longest-streak@^2.0.1: version "2.0.4" resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4" @@ -20768,7 +20781,12 @@ node-fetch@2.6.1, node-fetch@^1.0.1, node-fetch@^2.3.0, node-fetch@^2.6.1, node- dependencies: whatwg-url "^5.0.0" -node-forge@^0.10.0, node-forge@^1.3.0: +node-forge@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" + integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== + +node-forge@^1.2.1, node-forge@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.0.tgz#37a874ea723855f37db091e6c186e5b67a01d4b2" integrity sha512-08ARB91bUi6zNKzVmaj3QO7cr397uiDT2nJ63cHjyNtCTWIgvS47j3eT0WfzUwS9+6Z5YshRaoasFkXCKrIYbA== @@ -20815,20 +20833,20 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= -node-jose@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/node-jose/-/node-jose-2.0.0.tgz#541c6b52c387a3f18fc06cd502baad759af9c470" - integrity sha512-j8zoFze1gijl8+DK/dSXXqX7+o2lMYv1XS+ptnXgGV/eloQaqq1YjNtieepbKs9jBS4WTnMOqyKSaQuunJzx0A== +node-jose@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/node-jose/-/node-jose-2.1.0.tgz#a2d12a7ff2d386f23979c1bf77f939449ce073d8" + integrity sha512-Zmm8vFPJabphGBc5Wz1/LUMPS+1cynqw16RIhgVNQMEI2yEQrvl7Gx2EwN9GhP8tkm8f7SH53K2nIx8TeNTIdg== dependencies: base64url "^3.0.1" - buffer "^5.5.0" + buffer "^6.0.3" es6-promise "^4.2.8" - lodash "^4.17.15" - long "^4.0.0" - node-forge "^0.10.0" - pako "^1.0.11" + lodash "^4.17.21" + long "^5.2.0" + node-forge "^1.2.1" + pako "^2.0.4" process "^0.11.10" - uuid "^3.3.3" + uuid "^8.3.2" node-libs-browser@^2.2.1: version "2.2.1" @@ -21744,11 +21762,16 @@ pako@^0.2.5: resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" integrity sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU= -pako@^1.0.11, pako@^1.0.3, pako@^1.0.5, pako@~1.0.2, pako@~1.0.5: +pako@^1.0.3, pako@^1.0.5, pako@~1.0.2, pako@~1.0.5: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== +pako@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pako/-/pako-2.0.4.tgz#6cebc4bbb0b6c73b0d5b8d7e8476e2b2fbea576d" + integrity sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg== + papaparse@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-5.2.0.tgz#97976a1b135c46612773029153dc64995caa3b7b" From 23586d2e4f0039c5a95f70a8e51c01a1ae1485b7 Mon Sep 17 00:00:00 2001 From: Jordan <51442161+JordanSh@users.noreply.github.com> Date: Tue, 29 Mar 2022 12:32:19 +0300 Subject: [PATCH 09/37] CSP Viewing states (#128215) --- .../cloud_security_posture/common/types.ts | 2 +- .../public/common/api/index.ts | 2 +- .../api/use_cis_kubernetes_integration.tsx | 31 +++ ...s => use_compliance_dashboard_data_api.ts} | 6 +- .../public/components/chart_panel.tsx | 4 +- .../components/csp_page_template.test.tsx | 258 ++++++++++++++++++ .../public/components/csp_page_template.tsx | 202 ++++++++++++++ .../public/components/page_template.test.tsx | 158 ----------- .../public/components/page_template.tsx | 117 -------- .../public/components/translations.ts | 41 ++- .../public/components/unknown_route.tsx | 2 +- .../pages/benchmarks/benchmarks.test.tsx | 7 + .../public/pages/benchmarks/benchmarks.tsx | 5 +- .../compliance_charts/risks_table.tsx | 6 +- .../compliance_dashboard.tsx | 53 ++-- .../dashboard_sections/benchmarks_section.tsx | 42 +-- .../dashboard_sections/summary_section.tsx | 25 +- .../compliance_dashboard/translations.ts | 15 + .../public/pages/findings/findings.test.tsx | 5 + .../public/pages/findings/findings.tsx | 11 +- .../public/pages/rules/index.tsx | 17 +- .../public/pages/rules/rules.test.tsx | 7 + .../public/test/fixtures/react_query.ts | 8 +- .../compliance_dashboard.ts | 4 +- .../compliance_dashboard/get_clusters.test.ts | 2 +- .../compliance_dashboard/get_clusters.ts | 8 +- .../get_resources_types.test.ts | 2 +- .../get_resources_types.ts | 8 +- .../compliance_dashboard/get_stats.test.ts | 2 +- .../routes/compliance_dashboard/get_stats.ts | 8 +- 30 files changed, 646 insertions(+), 412 deletions(-) create mode 100644 x-pack/plugins/cloud_security_posture/public/common/api/use_cis_kubernetes_integration.tsx rename x-pack/plugins/cloud_security_posture/public/common/api/{use_cloud_posture_stats_api.ts => use_compliance_dashboard_data_api.ts} (71%) create mode 100644 x-pack/plugins/cloud_security_posture/public/components/csp_page_template.test.tsx create mode 100644 x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx delete mode 100644 x-pack/plugins/cloud_security_posture/public/components/page_template.test.tsx delete mode 100644 x-pack/plugins/cloud_security_posture/public/components/page_template.tsx diff --git a/x-pack/plugins/cloud_security_posture/common/types.ts b/x-pack/plugins/cloud_security_posture/common/types.ts index ca01d1ad927cd..b414b49ef30d3 100644 --- a/x-pack/plugins/cloud_security_posture/common/types.ts +++ b/x-pack/plugins/cloud_security_posture/common/types.ts @@ -35,7 +35,7 @@ export interface Cluster { resourcesTypes: ResourceType[]; } -export interface CloudPostureStats { +export interface ComplianceDashboardData { stats: Stats; resourcesTypes: ResourceType[]; clusters: Cluster[]; diff --git a/x-pack/plugins/cloud_security_posture/public/common/api/index.ts b/x-pack/plugins/cloud_security_posture/public/common/api/index.ts index 915881e2ab9b2..2468e095ff5f9 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/api/index.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/api/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export * from './use_cloud_posture_stats_api'; +export * from './use_compliance_dashboard_data_api'; diff --git a/x-pack/plugins/cloud_security_posture/public/common/api/use_cis_kubernetes_integration.tsx b/x-pack/plugins/cloud_security_posture/public/common/api/use_cis_kubernetes_integration.tsx new file mode 100644 index 0000000000000..09b77a20835f7 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/common/api/use_cis_kubernetes_integration.tsx @@ -0,0 +1,31 @@ +/* + * 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 { useQuery } from 'react-query'; +import { + epmRouteService, + type GetInfoResponse, + type DefaultPackagesInstallationError, +} from '../../../../fleet/common'; +import { CIS_KUBERNETES_PACKAGE_NAME } from '../../../common/constants'; +import { useKibana } from '../hooks/use_kibana'; + +export const CIS_KUBERNETES_INTEGRATION_VERSION = '0.0.1'; + +/** + * This hook will find our cis integration and return its PackageInfo + * */ +export const useCisKubernetesIntegration = () => { + const { http } = useKibana().services; + + return useQuery(['integrations'], () => + http.get( + epmRouteService.getInfoPath(CIS_KUBERNETES_PACKAGE_NAME, CIS_KUBERNETES_INTEGRATION_VERSION), + { query: { experimental: true } } + ) + ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/common/api/use_cloud_posture_stats_api.ts b/x-pack/plugins/cloud_security_posture/public/common/api/use_compliance_dashboard_data_api.ts similarity index 71% rename from x-pack/plugins/cloud_security_posture/public/common/api/use_cloud_posture_stats_api.ts rename to x-pack/plugins/cloud_security_posture/public/common/api/use_compliance_dashboard_data_api.ts index c1cc4a61fd7ae..91bedbf83769d 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/api/use_cloud_posture_stats_api.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/api/use_compliance_dashboard_data_api.ts @@ -7,12 +7,12 @@ import { useQuery } from 'react-query'; import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; -import { CloudPostureStats } from '../../../common/types'; +import { ComplianceDashboardData } from '../../../common/types'; import { STATS_ROUTE_PATH } from '../../../common/constants'; const getStatsKey = 'csp_dashboard_stats'; -export const useCloudPostureStatsApi = () => { +export const useComplianceDashboardDataApi = () => { const { http } = useKibana().services; - return useQuery([getStatsKey], () => http!.get(STATS_ROUTE_PATH)); + return useQuery([getStatsKey], () => http!.get(STATS_ROUTE_PATH)); }; diff --git a/x-pack/plugins/cloud_security_posture/public/components/chart_panel.tsx b/x-pack/plugins/cloud_security_posture/public/components/chart_panel.tsx index 2b0882d0916e6..54592880f47ff 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/chart_panel.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/chart_panel.tsx @@ -19,8 +19,8 @@ import { CHART_PANEL_TEST_SUBJECTS } from './constants'; interface ChartPanelProps { title?: string; hasBorder?: boolean; - isLoading: boolean; - isError: boolean; + isLoading?: boolean; + isError?: boolean; } const Loading = () => ( diff --git a/x-pack/plugins/cloud_security_posture/public/components/csp_page_template.test.tsx b/x-pack/plugins/cloud_security_posture/public/components/csp_page_template.test.tsx new file mode 100644 index 0000000000000..ef097f224ab5f --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/components/csp_page_template.test.tsx @@ -0,0 +1,258 @@ +/* + * 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 React, { type ComponentProps } from 'react'; +import { render, screen } from '@testing-library/react'; +import Chance from 'chance'; +import { coreMock } from '../../../../../src/core/public/mocks'; +import { createNavigationItemFixture } from '../test/fixtures/navigation_item'; +import { createReactQueryResponse } from '../test/fixtures/react_query'; +import { TestProvider } from '../test/test_provider'; +import { CspPageTemplate, getSideNavItems, isCommonError } from './csp_page_template'; +import { LOADING, PACKAGE_NOT_INSTALLED_TEXT, DEFAULT_NO_DATA_TEXT } from './translations'; +import { useCisKubernetesIntegration } from '../common/api/use_cis_kubernetes_integration'; +import { UseQueryResult } from 'react-query'; + +const chance = new Chance(); + +// Synchronized to the error message in the formatted message in `csp_page_template.tsx` +const ERROR_LOADING_DATA_DEFAULT_MESSAGE = "We couldn't fetch your cloud security posture data"; +const packageNotInstalledUniqueTexts = [ + PACKAGE_NOT_INSTALLED_TEXT.PAGE_TITLE, + PACKAGE_NOT_INSTALLED_TEXT.DESCRIPTION, +]; + +jest.mock('../common/api/use_cis_kubernetes_integration'); + +describe('getSideNavItems', () => { + it('maps navigation items to side navigation items', () => { + const navigationItem = createNavigationItemFixture(); + const id = chance.word(); + const sideNavItems = getSideNavItems({ [id]: navigationItem }); + + expect(sideNavItems).toHaveLength(1); + expect(sideNavItems[0]).toMatchObject({ + id, + name: navigationItem.name, + renderItem: expect.any(Function), + }); + }); + + it('does not map disabled navigation items to side navigation items', () => { + const navigationItem = createNavigationItemFixture({ disabled: true }); + const id = chance.word(); + const sideNavItems = getSideNavItems({ [id]: navigationItem }); + expect(sideNavItems).toHaveLength(0); + }); +}); + +describe('', () => { + beforeEach(() => { + jest.resetAllMocks(); + // if package installation status is 'not_installed', CspPageTemplate will render a noDataConfig prompt + (useCisKubernetesIntegration as jest.Mock).mockImplementation(() => ({ + data: { item: { status: 'installed' } }, + })); + }); + + const renderCspPageTemplate = (props: ComponentProps = {}) => { + const mockCore = coreMock.createStart(); + + render( + + + + ); + }; + + it('renders children if integration is installed', () => { + const children = chance.sentence(); + renderCspPageTemplate({ children }); + + expect(screen.getByText(children)).toBeInTheDocument(); + expect(screen.queryByText(LOADING)).not.toBeInTheDocument(); + expect(screen.queryByText(ERROR_LOADING_DATA_DEFAULT_MESSAGE)).not.toBeInTheDocument(); + packageNotInstalledUniqueTexts.forEach((text) => + expect(screen.queryByText(text)).not.toBeInTheDocument() + ); + }); + + it('renders integrations installation prompt if integration is not installed', () => { + (useCisKubernetesIntegration as jest.Mock).mockImplementation(() => ({ + isSuccess: true, + data: { item: { status: 'not_installed' } }, + })); + + const children = chance.sentence(); + renderCspPageTemplate({ children }); + + Object.values(PACKAGE_NOT_INSTALLED_TEXT).forEach((text) => + expect(screen.getByText(text)).toBeInTheDocument() + ); + expect(screen.queryByText(children)).not.toBeInTheDocument(); + expect(screen.queryByText(LOADING)).not.toBeInTheDocument(); + expect(screen.queryByText(ERROR_LOADING_DATA_DEFAULT_MESSAGE)).not.toBeInTheDocument(); + }); + + it('renders default loading text when query isLoading', () => { + const query = createReactQueryResponse({ + status: 'loading', + }) as unknown as UseQueryResult; + + const children = chance.sentence(); + renderCspPageTemplate({ children, query }); + + expect(screen.getByText(LOADING)).toBeInTheDocument(); + expect(screen.queryByText(children)).not.toBeInTheDocument(); + expect(screen.queryByText(ERROR_LOADING_DATA_DEFAULT_MESSAGE)).not.toBeInTheDocument(); + packageNotInstalledUniqueTexts.forEach((text) => + expect(screen.queryByText(text)).not.toBeInTheDocument() + ); + }); + + it('renders default error texts when query isError', () => { + const error = chance.sentence(); + const message = chance.sentence(); + const statusCode = chance.integer(); + + const query = createReactQueryResponse({ + status: 'error', + error: { + body: { + error, + message, + statusCode, + }, + }, + }) as unknown as UseQueryResult; + + const children = chance.sentence(); + renderCspPageTemplate({ children, query }); + + [ERROR_LOADING_DATA_DEFAULT_MESSAGE, error, message, statusCode].forEach((text) => + expect(screen.getByText(text, { exact: false })).toBeInTheDocument() + ); + expect(screen.queryByText(LOADING)).not.toBeInTheDocument(); + expect(screen.queryByText(children)).not.toBeInTheDocument(); + packageNotInstalledUniqueTexts.forEach((text) => + expect(screen.queryByText(text)).not.toBeInTheDocument() + ); + }); + + it('prefers custom error render', () => { + const error = chance.sentence(); + const message = chance.sentence(); + const statusCode = chance.integer(); + + const query = createReactQueryResponse({ + status: 'error', + error: { + body: { + error, + message, + statusCode, + }, + }, + }) as unknown as UseQueryResult; + + const children = chance.sentence(); + renderCspPageTemplate({ + children, + query, + errorRender: (err) =>
{isCommonError(err) && err.body.message}
, + }); + + expect(screen.getByText(message)).toBeInTheDocument(); + [ERROR_LOADING_DATA_DEFAULT_MESSAGE, error, statusCode].forEach((text) => + expect(screen.queryByText(text)).not.toBeInTheDocument() + ); + expect(screen.queryByText(LOADING)).not.toBeInTheDocument(); + expect(screen.queryByText(children)).not.toBeInTheDocument(); + packageNotInstalledUniqueTexts.forEach((text) => + expect(screen.queryByText(text)).not.toBeInTheDocument() + ); + }); + + it('prefers custom loading render', () => { + const loading = chance.sentence(); + + const query = createReactQueryResponse({ + status: 'loading', + }) as unknown as UseQueryResult; + + const children = chance.sentence(); + renderCspPageTemplate({ + children, + query, + loadingRender: () =>
{loading}
, + }); + + expect(screen.getByText(loading)).toBeInTheDocument(); + expect(screen.queryByText(ERROR_LOADING_DATA_DEFAULT_MESSAGE)).not.toBeInTheDocument(); + expect(screen.queryByText(LOADING)).not.toBeInTheDocument(); + expect(screen.queryByText(children)).not.toBeInTheDocument(); + packageNotInstalledUniqueTexts.forEach((text) => + expect(screen.queryByText(text)).not.toBeInTheDocument() + ); + }); + + it('renders noDataConfig prompt when query data is undefined', () => { + const query = createReactQueryResponse({ + status: 'success', + data: undefined, + }) as unknown as UseQueryResult; + + const children = chance.sentence(); + renderCspPageTemplate({ children, query }); + + expect(screen.getByText(DEFAULT_NO_DATA_TEXT.PAGE_TITLE)).toBeInTheDocument(); + expect(screen.queryByText(LOADING)).not.toBeInTheDocument(); + expect(screen.queryByText(children)).not.toBeInTheDocument(); + expect(screen.queryByText(ERROR_LOADING_DATA_DEFAULT_MESSAGE)).not.toBeInTheDocument(); + packageNotInstalledUniqueTexts.forEach((text) => + expect(screen.queryByText(text)).not.toBeInTheDocument() + ); + }); + + it('prefers custom noDataConfig prompt', () => { + const pageTitle = chance.sentence(); + const solution = chance.sentence(); + const docsLink = chance.sentence(); + + const query = createReactQueryResponse({ + status: 'success', + data: undefined, + }) as unknown as UseQueryResult; + + const children = chance.sentence(); + renderCspPageTemplate({ + children, + query, + noDataConfig: { pageTitle, solution, docsLink, actions: {} }, + }); + + expect(screen.getByText(pageTitle)).toBeInTheDocument(); + expect(screen.getByText(solution, { exact: false })).toBeInTheDocument(); + expect(screen.queryByText(LOADING)).not.toBeInTheDocument(); + expect(screen.queryByText(children)).not.toBeInTheDocument(); + expect(screen.queryByText(ERROR_LOADING_DATA_DEFAULT_MESSAGE)).not.toBeInTheDocument(); + packageNotInstalledUniqueTexts.forEach((text) => + expect(screen.queryByText(text)).not.toBeInTheDocument() + ); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx b/x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx new file mode 100644 index 0000000000000..8d1d96f2cb9f7 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx @@ -0,0 +1,202 @@ +/* + * 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 React from 'react'; +import type { UseQueryResult } from 'react-query'; +import { NavLink } from 'react-router-dom'; +import { EuiEmptyPrompt, EuiErrorBoundary, EuiTitle } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { + KibanaPageTemplate, + type KibanaPageTemplateProps, +} from '../../../../../src/plugins/kibana_react/public'; +import { allNavigationItems } from '../common/navigation/constants'; +import type { CspNavigationItem } from '../common/navigation/types'; +import { CLOUD_SECURITY_POSTURE } from '../common/translations'; +import { CspLoadingState } from './csp_loading_state'; +import { DEFAULT_NO_DATA_TEXT, LOADING, PACKAGE_NOT_INSTALLED_TEXT } from './translations'; +import { useCisKubernetesIntegration } from '../common/api/use_cis_kubernetes_integration'; +import { useCISIntegrationLink } from '../common/navigation/use_navigate_to_cis_integration'; + +export interface CommonError { + body: { + error: string; + message: string; + statusCode: number; + }; +} + +export const isCommonError = (x: any): x is CommonError => { + if (!('body' in x)) return false; + + const { + body: { error, message, statusCode }, + } = x; + + return !!(error && message && statusCode); +}; + +const activeItemStyle = { fontWeight: 700 }; + +export const getSideNavItems = ( + navigationItems: Record +): NonNullable['items'] => + Object.entries(navigationItems) + .filter(([_, navigationItem]) => !navigationItem.disabled) + .map(([id, navigationItem]) => ({ + id, + name: navigationItem.name, + renderItem: () => ( + + {navigationItem.name} + + ), + })); + +const DEFAULT_PAGE_PROPS: KibanaPageTemplateProps = { + solutionNav: { + name: CLOUD_SECURITY_POSTURE, + items: getSideNavItems({ + dashboard: allNavigationItems.dashboard, + findings: allNavigationItems.findings, + benchmark: allNavigationItems.benchmarks, + }), + }, + restrictWidth: false, +}; + +export const DEFAULT_NO_DATA_CONFIG: KibanaPageTemplateProps['noDataConfig'] = { + pageTitle: DEFAULT_NO_DATA_TEXT.PAGE_TITLE, + solution: DEFAULT_NO_DATA_TEXT.SOLUTION, + // TODO: Add real docs link once we have it + docsLink: 'https://www.elastic.co/guide/index.html', + logo: 'logoSecurity', + actions: {}, +}; + +const getPackageNotInstalledNoDataConfig = ( + cisIntegrationLink: string +): KibanaPageTemplateProps['noDataConfig'] => ({ + pageTitle: PACKAGE_NOT_INSTALLED_TEXT.PAGE_TITLE, + solution: PACKAGE_NOT_INSTALLED_TEXT.SOLUTION, + // TODO: Add real docs link once we have it + docsLink: 'https://www.elastic.co/guide/index.html', + logo: 'logoSecurity', + actions: { + elasticAgent: { + href: cisIntegrationLink, + title: PACKAGE_NOT_INSTALLED_TEXT.BUTTON_TITLE, + description: PACKAGE_NOT_INSTALLED_TEXT.DESCRIPTION, + }, + }, +}); + +const DefaultLoading = () => {LOADING}; + +const DefaultError = (error: unknown) => ( + + +

+ +

+
+ {isCommonError(error) && ( + <> + +
+ +
+
+ +
+ +
+
+ + )} + + } + /> +); + +export const CspPageTemplate = ({ + query, + children, + loadingRender = DefaultLoading, + errorRender = DefaultError, + ...kibanaPageTemplateProps +}: KibanaPageTemplateProps & { + loadingRender?: () => React.ReactNode; + errorRender?: (error: TError) => React.ReactNode; + query?: UseQueryResult; +}) => { + const cisKubernetesPackageInfo = useCisKubernetesIntegration(); + const cisIntegrationLink = useCISIntegrationLink(); + + const getNoDataConfig = (): KibanaPageTemplateProps['noDataConfig'] => { + if ( + cisKubernetesPackageInfo?.isSuccess && + cisKubernetesPackageInfo.data.item.status === 'not_installed' + ) { + return getPackageNotInstalledNoDataConfig(cisIntegrationLink); + } + + // when query was successful, but data is undefined + if (query?.isSuccess && !query?.data) { + return kibanaPageTemplateProps.noDataConfig || DEFAULT_NO_DATA_CONFIG; + } + + // when the consumer didn't pass a query, most likely to handle the render on his own + if (!query) return kibanaPageTemplateProps.noDataConfig; + }; + + const getTemplate = (): KibanaPageTemplateProps['template'] => { + if (query?.isLoading || query?.isError) return 'centeredContent'; + + return kibanaPageTemplateProps.template || 'default'; + }; + + const render = () => { + if (query?.isLoading) return loadingRender(); + if (query?.isError) return errorRender(query.error); + if (query?.isSuccess) return children; + + return children; + }; + + return ( + + + {cisKubernetesPackageInfo?.data?.item.status === 'installed' && render()} + + + ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/components/page_template.test.tsx b/x-pack/plugins/cloud_security_posture/public/components/page_template.test.tsx deleted file mode 100644 index 1c367cd5c57f0..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/components/page_template.test.tsx +++ /dev/null @@ -1,158 +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 React, { type ComponentProps } from 'react'; -import { render, screen } from '@testing-library/react'; -import Chance from 'chance'; -import { coreMock } from '../../../../../src/core/public/mocks'; -import { createStubDataView } from '../../../../../src/plugins/data_views/public/data_views/data_view.stub'; -import { CSP_KUBEBEAT_INDEX_PATTERN } from '../../common/constants'; -import { useKubebeatDataView } from '../common/api/use_kubebeat_data_view'; -import { createNavigationItemFixture } from '../test/fixtures/navigation_item'; -import { createReactQueryResponse } from '../test/fixtures/react_query'; -import { TestProvider } from '../test/test_provider'; -import { CspPageTemplate, getSideNavItems } from './page_template'; -import { - LOADING, - NO_DATA_CONFIG_BUTTON, - NO_DATA_CONFIG_DESCRIPTION, - NO_DATA_CONFIG_TITLE, -} from './translations'; - -const chance = new Chance(); - -const BLANK_PAGE_GRAPHIC_TEXTS = [ - NO_DATA_CONFIG_TITLE, - NO_DATA_CONFIG_DESCRIPTION, - NO_DATA_CONFIG_BUTTON, -]; - -// Synchronized to the error message in the formatted message in `page_template.tsx` -const ERROR_LOADING_DATA_DEFAULT_MESSAGE = "We couldn't fetch your cloud security posture data"; - -jest.mock('../common/api/use_kubebeat_data_view'); - -describe('getSideNavItems', () => { - it('maps navigation items to side navigation items', () => { - const navigationItem = createNavigationItemFixture(); - const id = chance.word(); - const sideNavItems = getSideNavItems({ [id]: navigationItem }); - - expect(sideNavItems).toHaveLength(1); - expect(sideNavItems[0]).toMatchObject({ - id, - name: navigationItem.name, - renderItem: expect.any(Function), - }); - }); - - it('does not map disabled navigation items to side navigation items', () => { - const navigationItem = createNavigationItemFixture({ disabled: true }); - const id = chance.word(); - const sideNavItems = getSideNavItems({ [id]: navigationItem }); - expect(sideNavItems).toHaveLength(0); - }); -}); - -describe('', () => { - beforeEach(() => { - jest.resetAllMocks(); - }); - - const renderCspPageTemplate = (props: ComponentProps = {}) => { - const mockCore = coreMock.createStart(); - - render( - - - - ); - }; - - it('renders children when data view is found', () => { - (useKubebeatDataView as jest.Mock).mockImplementation(() => - createReactQueryResponse({ - status: 'success', - data: createStubDataView({ - spec: { - id: CSP_KUBEBEAT_INDEX_PATTERN, - }, - }), - }) - ); - - const children = chance.sentence(); - renderCspPageTemplate({ children }); - - expect(screen.getByText(children)).toBeInTheDocument(); - expect(screen.queryByText(LOADING)).not.toBeInTheDocument(); - expect(screen.queryByText(ERROR_LOADING_DATA_DEFAULT_MESSAGE)).not.toBeInTheDocument(); - BLANK_PAGE_GRAPHIC_TEXTS.forEach((blankPageGraphicText) => - expect(screen.queryByText(blankPageGraphicText)).not.toBeInTheDocument() - ); - }); - - it('renders loading text when data view is loading', () => { - (useKubebeatDataView as jest.Mock).mockImplementation(() => - createReactQueryResponse({ status: 'loading' }) - ); - - const children = chance.sentence(); - renderCspPageTemplate({ children }); - - expect(screen.getByText(LOADING)).toBeInTheDocument(); - expect(screen.queryByText(children)).not.toBeInTheDocument(); - expect(screen.queryByText(ERROR_LOADING_DATA_DEFAULT_MESSAGE)).not.toBeInTheDocument(); - BLANK_PAGE_GRAPHIC_TEXTS.forEach((blankPageGraphicText) => - expect(screen.queryByText(blankPageGraphicText)).not.toBeInTheDocument() - ); - }); - - it('renders an error view when data view fetching has an error', () => { - (useKubebeatDataView as jest.Mock).mockImplementation(() => - createReactQueryResponse({ status: 'error', error: new Error('') }) - ); - - const children = chance.sentence(); - renderCspPageTemplate({ children }); - - expect(screen.getByText(ERROR_LOADING_DATA_DEFAULT_MESSAGE)).toBeInTheDocument(); - expect(screen.queryByText(LOADING)).not.toBeInTheDocument(); - expect(screen.queryByText(children)).not.toBeInTheDocument(); - BLANK_PAGE_GRAPHIC_TEXTS.forEach((blankPageGraphicText) => - expect(screen.queryByText(blankPageGraphicText)).not.toBeInTheDocument() - ); - }); - - it('renders the blank page graphic when data view is missing', () => { - (useKubebeatDataView as jest.Mock).mockImplementation(() => - createReactQueryResponse({ - status: 'success', - data: undefined, - }) - ); - - const children = chance.sentence(); - renderCspPageTemplate({ children }); - - BLANK_PAGE_GRAPHIC_TEXTS.forEach((text) => expect(screen.getByText(text)).toBeInTheDocument()); - expect(screen.queryByText(ERROR_LOADING_DATA_DEFAULT_MESSAGE)).not.toBeInTheDocument(); - expect(screen.queryByText(LOADING)).not.toBeInTheDocument(); - expect(screen.queryByText(children)).not.toBeInTheDocument(); - }); -}); diff --git a/x-pack/plugins/cloud_security_posture/public/components/page_template.tsx b/x-pack/plugins/cloud_security_posture/public/components/page_template.tsx deleted file mode 100644 index 45cae3f996e36..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/components/page_template.tsx +++ /dev/null @@ -1,117 +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 React from 'react'; -import { NavLink } from 'react-router-dom'; -import { EuiEmptyPrompt, EuiErrorBoundary, EuiTitle } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { - KibanaPageTemplate, - type KibanaPageTemplateProps, -} from '../../../../../src/plugins/kibana_react/public'; -import { useKubebeatDataView } from '../common/api/use_kubebeat_data_view'; -import { allNavigationItems } from '../common/navigation/constants'; -import type { CspNavigationItem } from '../common/navigation/types'; -import { useCISIntegrationLink } from '../common/navigation/use_navigate_to_cis_integration'; -import { CLOUD_SECURITY_POSTURE } from '../common/translations'; -import { CspLoadingState } from './csp_loading_state'; -import { - LOADING, - NO_DATA_CONFIG_BUTTON, - NO_DATA_CONFIG_DESCRIPTION, - NO_DATA_CONFIG_SOLUTION_NAME, - NO_DATA_CONFIG_TITLE, -} from './translations'; - -const activeItemStyle = { fontWeight: 700 }; - -export const getSideNavItems = ( - navigationItems: Record -): NonNullable['items'] => - Object.entries(navigationItems) - .filter(([_, navigationItem]) => !navigationItem.disabled) - .map(([id, navigationItem]) => ({ - id, - name: navigationItem.name, - renderItem: () => ( - - {navigationItem.name} - - ), - })); - -const DEFAULT_PROPS: KibanaPageTemplateProps = { - solutionNav: { - name: CLOUD_SECURITY_POSTURE, - items: getSideNavItems({ - dashboard: allNavigationItems.dashboard, - findings: allNavigationItems.findings, - benchmark: allNavigationItems.benchmarks, - }), - }, - restrictWidth: false, -}; - -const getNoDataConfig = (cisIntegrationLink: string): KibanaPageTemplateProps['noDataConfig'] => ({ - pageTitle: NO_DATA_CONFIG_TITLE, - solution: NO_DATA_CONFIG_SOLUTION_NAME, - // TODO: Add real docs link once we have it - docsLink: 'https://www.elastic.co/guide/index.html', - logo: 'logoSecurity', - actions: { - elasticAgent: { - href: cisIntegrationLink, - title: NO_DATA_CONFIG_BUTTON, - description: NO_DATA_CONFIG_DESCRIPTION, - }, - }, -}); - -export const CspPageTemplate: React.FC = ({ children, ...props }) => { - // TODO: Consider using more sophisticated logic to find out if our integration is installed - const kubeBeatQuery = useKubebeatDataView(); - const cisIntegrationLink = useCISIntegrationLink(); - - let noDataConfig: KibanaPageTemplateProps['noDataConfig']; - if (kubeBeatQuery.status === 'success' && !kubeBeatQuery.data) { - noDataConfig = getNoDataConfig(cisIntegrationLink); - } - - let template: KibanaPageTemplateProps['template'] = 'default'; - if (kubeBeatQuery.status === 'error' || kubeBeatQuery.status === 'loading') { - template = 'centeredContent'; - } - - return ( - - - {kubeBeatQuery.status === 'loading' && {LOADING}} - {kubeBeatQuery.status === 'error' && ( - -

- -

- - } - /> - )} - {kubeBeatQuery.status === 'success' && children} -
-
- ); -}; diff --git a/x-pack/plugins/cloud_security_posture/public/components/translations.ts b/x-pack/plugins/cloud_security_posture/public/components/translations.ts index b203d0365a28f..facbbcc3176da 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/translations.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/translations.ts @@ -40,28 +40,27 @@ export const CSP_EVALUATION_BADGE_PASSED = i18n.translate( } ); -export const NO_DATA_CONFIG_TITLE = i18n.translate('xpack.csp.pageTemplate.noDataConfigTitle', { - defaultMessage: 'Understand your cloud security posture', -}); - -export const NO_DATA_CONFIG_SOLUTION_NAME = i18n.translate( - 'xpack.csp.pageTemplate.noDataConfig.solutionNameLabel', - { +export const PACKAGE_NOT_INSTALLED_TEXT = { + PAGE_TITLE: i18n.translate('xpack.csp.cspPageTemplate.packageNotInstalled.pageTitle', { + defaultMessage: 'Install Integration to get started', + }), + SOLUTION: i18n.translate('xpack.csp.cspPageTemplate.packageNotInstalled.solutionNameLabel', { defaultMessage: 'Cloud Security Posture', - } -); - -export const NO_DATA_CONFIG_DESCRIPTION = i18n.translate( - 'xpack.csp.pageTemplate.noDataConfigDescription', - { + }), + BUTTON_TITLE: i18n.translate('xpack.csp.cspPageTemplate.packageNotInstalled.buttonLabel', { + defaultMessage: 'Add a CIS integration', + }), + DESCRIPTION: i18n.translate('xpack.csp.cspPageTemplate.packageNotInstalled.description', { defaultMessage: 'Use our CIS Kubernetes Benchmark integration to measure your Kubernetes cluster setup against the CIS recommendations.', - } -); + }), +}; -export const NO_DATA_CONFIG_BUTTON = i18n.translate( - 'xpack.csp.pageTemplate.noDataConfigButtonLabel', - { - defaultMessage: 'Add a CIS integration', - } -); +export const DEFAULT_NO_DATA_TEXT = { + PAGE_TITLE: i18n.translate('xpack.csp.cspPageTemplate.defaultNoDataConfig.pageTitle', { + defaultMessage: 'No data found', + }), + SOLUTION: i18n.translate('xpack.csp.cspPageTemplate.defaultNoDataConfig.solutionNameLabel', { + defaultMessage: 'Cloud Security Posture', + }), +}; diff --git a/x-pack/plugins/cloud_security_posture/public/components/unknown_route.tsx b/x-pack/plugins/cloud_security_posture/public/components/unknown_route.tsx index 248d598302b9f..90a469ba2dd48 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/unknown_route.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/unknown_route.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { EuiEmptyPrompt } from '@elastic/eui'; -import { CspPageTemplate } from './page_template'; +import { CspPageTemplate } from './csp_page_template'; import * as TEXT from './translations'; export const UnknownRoute = React.memo(() => ( diff --git a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.test.tsx index 983c58f2d5d7c..918294794e9d2 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.test.tsx @@ -21,13 +21,20 @@ import { TABLE_COLUMN_HEADERS, } from './translations'; import { useCspBenchmarkIntegrations } from './use_csp_benchmark_integrations'; +import { useCisKubernetesIntegration } from '../../common/api/use_cis_kubernetes_integration'; jest.mock('./use_csp_benchmark_integrations'); jest.mock('../../common/api/use_kubebeat_data_view'); +jest.mock('../../common/api/use_cis_kubernetes_integration'); describe('', () => { beforeEach(() => { jest.resetAllMocks(); + + // if package installation status is 'not_installed', CspPageTemplate will render a noDataConfig prompt + (useCisKubernetesIntegration as jest.Mock).mockImplementation(() => ({ + data: { item: { status: 'installed' } }, + })); // Required for the page template to render the benchmarks page (useKubebeatDataView as jest.Mock).mockImplementation(() => createReactQueryResponse({ diff --git a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.tsx b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.tsx index e7f8991eedf8f..e303c619a8d01 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.tsx @@ -4,6 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + +import React, { useState } from 'react'; import { EuiFieldSearch, EuiFieldSearchProps, @@ -15,13 +17,12 @@ import { EuiTextColor, EuiText, } from '@elastic/eui'; -import React, { useState } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import useDebounce from 'react-use/lib/useDebounce'; import { allNavigationItems } from '../../common/navigation/constants'; import { useCspBreadcrumbs } from '../../common/navigation/use_csp_breadcrumbs'; import { useCISIntegrationLink } from '../../common/navigation/use_navigate_to_cis_integration'; -import { CspPageTemplate } from '../../components/page_template'; +import { CspPageTemplate } from '../../components/csp_page_template'; import { BenchmarksTable } from './benchmarks_table'; import { ADD_A_CIS_INTEGRATION, BENCHMARK_INTEGRATIONS } from './translations'; import { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/risks_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/risks_table.tsx index 1e355b3f3c82f..086fea34a2c50 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/risks_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/risks_table.tsx @@ -14,7 +14,7 @@ import { EuiLink, EuiText, } from '@elastic/eui'; -import { CloudPostureStats, ResourceType } from '../../../../common/types'; +import { ComplianceDashboardData, ResourceType } from '../../../../common/types'; import { CompactFormattedNumber } from '../../../components/compact_formatted_number'; import * as TEXT from '../translations'; import { INTERNAL_FEATURE_FLAGS } from '../../../../common/constants'; @@ -59,14 +59,14 @@ const mockData = [ ]; export interface RisksTableProps { - data: CloudPostureStats['resourcesTypes']; + data: ComplianceDashboardData['resourcesTypes']; maxItems: number; onCellClick: (resourceTypeName: string) => void; onViewAllClick: () => void; } export const getTopRisks = ( - resourcesTypes: CloudPostureStats['resourcesTypes'], + resourcesTypes: ComplianceDashboardData['resourcesTypes'], maxItems: number ) => { const filtered = resourcesTypes.filter((x) => x.totalFailed > 0); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx index 07b5294a8d4ae..ff564744384e0 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx @@ -6,40 +6,51 @@ */ import React from 'react'; -import { EuiSpacer } from '@elastic/eui'; +import { EuiSpacer, EuiIcon } from '@elastic/eui'; import { allNavigationItems } from '../../common/navigation/constants'; import { useCspBreadcrumbs } from '../../common/navigation/use_csp_breadcrumbs'; import { SummarySection } from './dashboard_sections/summary_section'; import { BenchmarksSection } from './dashboard_sections/benchmarks_section'; -import { useCloudPostureStatsApi } from '../../common/api'; -import { CspPageTemplate } from '../../components/page_template'; -import * as TEXT from './translations'; +import { useComplianceDashboardDataApi } from '../../common/api'; +import { CspPageTemplate } from '../../components/csp_page_template'; +import { type KibanaPageTemplateProps } from '../../../../../../src/plugins/kibana_react/public'; +import { CLOUD_POSTURE, NO_DATA_CONFIG_TEXT } from './translations'; -const CompliancePage = () => { - const getStats = useCloudPostureStatsApi(); - if (getStats.isLoading) return null; - - return ( - <> - - - - - - ); -}; +const getNoDataConfig = (onClick: () => void): KibanaPageTemplateProps['noDataConfig'] => ({ + pageTitle: NO_DATA_CONFIG_TEXT.PAGE_TITLE, + solution: NO_DATA_CONFIG_TEXT.SOLUTION, + // TODO: Add real docs link once we have it + docsLink: 'https://www.elastic.co/guide/index.html', + logo: 'logoSecurity', + actions: { + dashboardNoDataCard: { + icon: , + onClick, + title: NO_DATA_CONFIG_TEXT.BUTTON_TITLE, + description: NO_DATA_CONFIG_TEXT.DESCRIPTION, + }, + }, +}); export const ComplianceDashboard = () => { + const getDashboardDataQuery = useComplianceDashboardDataApi(); useCspBreadcrumbs([allNavigationItems.dashboard]); return ( - + {getDashboardDataQuery.data && ( + <> + + + + + + )} ); }; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx index 0fc8f0d3d0bef..565811bca7ee7 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx @@ -17,34 +17,25 @@ import { useEuiTheme, } from '@elastic/eui'; import moment from 'moment'; -import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; import { PartitionElementEvent } from '@elastic/charts'; import { EuiThemeComputed } from '@elastic/eui/src/services/theme/types'; import { CloudPostureScoreChart } from '../compliance_charts/cloud_posture_score_chart'; -import { useCloudPostureStatsApi } from '../../../common/api/use_cloud_posture_stats_api'; import { ChartPanel } from '../../../components/chart_panel'; import * as TEXT from '../translations'; -import { Evaluation } from '../../../../common/types'; +import type { ComplianceDashboardData, Evaluation } from '../../../../common/types'; import { RisksTable } from '../compliance_charts/risks_table'; import { INTERNAL_FEATURE_FLAGS, RULE_FAILED } from '../../../../common/constants'; import { useNavigateFindings } from '../../../common/hooks/use_navigate_findings'; -const logoMap: ReadonlyMap = new Map([['CIS Kubernetes', 'logoKubernetes']]); - -const getBenchmarkLogo = (benchmarkName: string): EuiIconType => { - return logoMap.get(benchmarkName) ?? 'logoElastic'; -}; - -const mockClusterId = '2468540'; - const cardHeight = 300; -export const BenchmarksSection = () => { +export const BenchmarksSection = ({ + complianceData, +}: { + complianceData: ComplianceDashboardData; +}) => { const { euiTheme } = useEuiTheme(); const navToFindings = useNavigateFindings(); - const getStats = useCloudPostureStatsApi(); - const clusters = getStats.isSuccess && getStats.data.clusters; - if (!clusters) return null; const handleElementClick = (clusterId: string, elements: PartitionElementEvent[]) => { const [element] = elements; @@ -68,7 +59,7 @@ export const BenchmarksSection = () => { return ( <> - {clusters.map((cluster) => { + {complianceData.clusters.map((cluster) => { const shortId = cluster.meta.clusterId.slice(0, 6); return ( @@ -82,7 +73,7 @@ export const BenchmarksSection = () => {

{cluster.meta.benchmarkName}

-

{`Cluster ID ${shortId || mockClusterId}`}

+

{`Cluster ID ${shortId}`}

@@ -91,7 +82,8 @@ export const BenchmarksSection = () => { - + {/* TODO: change default k8s logo to use a getBenchmarkLogo function */} + {INTERNAL_FEATURE_FLAGS.showManageRulesMock && ( @@ -104,12 +96,7 @@ export const BenchmarksSection = () => { grow={4} style={{ borderRight: `1px solid ${euiTheme.colors.lightShade}` }} > - + { - + { +export const SummarySection = ({ complianceData }: { complianceData: ComplianceDashboardData }) => { const navToFindings = useNavigateFindings(); - const getStats = useCloudPostureStatsApi(); - if (!getStats.isSuccess) return null; const handleElementClick = (elements: PartitionElementEvent[]) => { const [element] = elements; @@ -49,22 +46,18 @@ export const SummarySection = () => { return ( - + - + { - + diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/translations.ts b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/translations.ts index 87193ef67fa3a..0e87681037c66 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/translations.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/translations.ts @@ -82,3 +82,18 @@ export const RESOURCE_TYPE = i18n.translate('xpack.csp.resource_type', { export const FINDINGS = i18n.translate('xpack.csp.findings', { defaultMessage: 'Findings', }); + +export const NO_DATA_CONFIG_TEXT = { + PAGE_TITLE: i18n.translate('xpack.csp.complianceDashboard.noDataConfig.pageTitle', { + defaultMessage: 'Cloud Security Compliance Dashboard', + }), + SOLUTION: i18n.translate('xpack.csp.complianceDashboard.noDataConfig.solutionNameLabel', { + defaultMessage: 'Cloud Security Posture', + }), + BUTTON_TITLE: i18n.translate('xpack.csp.complianceDashboard.noDataConfig.actionTitle', { + defaultMessage: 'Try Again', + }), + DESCRIPTION: i18n.translate('xpack.csp.complianceDashboard.noDataConfig.actionDescription', { + defaultMessage: 'You can try to refetch your data', + }), +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.test.tsx index 54f1ecf9f31ed..1094d5fa847de 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.test.tsx @@ -17,9 +17,11 @@ import { import { createStubDataView } from '../../../../../../src/plugins/data_views/public/data_views/data_view.stub'; import { CSP_KUBEBEAT_INDEX_PATTERN } from '../../../common/constants'; import * as TEST_SUBJECTS from './test_subjects'; +import { useCisKubernetesIntegration } from '../../common/api/use_cis_kubernetes_integration'; import type { DataView } from '../../../../../../src/plugins/data/common'; jest.mock('../../common/api/use_kubebeat_data_view'); +jest.mock('../../common/api/use_cis_kubernetes_integration'); beforeEach(() => { jest.restoreAllMocks(); @@ -36,6 +38,9 @@ describe('', () => { const data = dataPluginMock.createStartContract(); const source = await data.search.searchSource.create(); + (useCisKubernetesIntegration as jest.Mock).mockImplementation(() => ({ + data: { item: { status: 'installed' } }, + })); (source.fetch$ as jest.Mock).mockReturnValue({ toPromise: () => Promise.resolve({ rawResponse: { hits: { hits: [] } } }), }); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.tsx index 6ed043361b44f..fba8fc37f54d1 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.tsx @@ -10,7 +10,7 @@ import { useKubebeatDataView } from '../../common/api/use_kubebeat_data_view'; import { allNavigationItems } from '../../common/navigation/constants'; import { useCspBreadcrumbs } from '../../common/navigation/use_csp_breadcrumbs'; import { FindingsContainer } from './findings_container'; -import { CspPageTemplate } from '../../components/page_template'; +import { CspPageTemplate } from '../../components/csp_page_template'; import { FINDINGS } from './translations'; const pageHeader: EuiPageHeaderProps = { @@ -18,15 +18,12 @@ const pageHeader: EuiPageHeaderProps = { }; export const Findings = () => { - const dataView = useKubebeatDataView(); + const dataViewQuery = useKubebeatDataView(); useCspBreadcrumbs([allNavigationItems.findings]); return ( - // `CspPageTemplate` takes care of loading and error states for the kubebeat data view, no need to handle them here - - {dataView.status === 'success' && dataView.data && ( - - )} + + {dataViewQuery.data && } ); }; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/index.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/index.tsx index bcfc06e8e16a5..288f9e2f30c01 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/index.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/index.tsx @@ -4,19 +4,19 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import React, { useMemo } from 'react'; import { RouteComponentProps } from 'react-router-dom'; import { EuiTextColor, EuiEmptyPrompt } from '@elastic/eui'; import * as t from 'io-ts'; -import { CspPageTemplate } from '../../components/page_template'; import { RulesContainer, type PageUrlParams } from './rules_container'; import { allNavigationItems } from '../../common/navigation/constants'; import { useCspBreadcrumbs } from '../../common/navigation/use_csp_breadcrumbs'; import type { KibanaPageTemplateProps } from '../../../../../../src/plugins/kibana_react/public'; -import { CspLoadingState } from '../../components/csp_loading_state'; import { CspNavigationItem } from '../../common/navigation/types'; import { extractErrorMessage } from '../../../common/utils/helpers'; import { useCspIntegration } from './use_csp_integration'; +import { CspPageTemplate } from '../../components/csp_page_template'; const getRulesBreadcrumbs = (name?: string): CspNavigationItem[] => [allNavigationItems.benchmarks, { ...allNavigationItems.rules, name }].filter( @@ -35,7 +35,6 @@ export const Rules = ({ match: { params } }: RouteComponentProps) const pageProps: KibanaPageTemplateProps = useMemo( () => ({ - template: integrationInfo.status !== 'success' ? 'centeredContent' : undefined, pageHeader: { bottomBorder: false, // TODO: border still shows. pageTitle: 'Rules', @@ -46,16 +45,16 @@ export const Rules = ({ match: { params } }: RouteComponentProps) ), }, }), - [integrationInfo.data, integrationInfo.status] + [integrationInfo.data] ); return ( - + } + > {integrationInfo.status === 'success' && } - {integrationInfo.status === 'error' && ( - - )} - {integrationInfo.status === 'loading' && } ); }; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules.test.tsx index aaf7bdc557e21..bf5badfca1d95 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules.test.tsx @@ -15,10 +15,12 @@ import { type RouteComponentProps } from 'react-router-dom'; import { cspLoadingStateTestId } from '../../components/csp_loading_state'; import type { PageUrlParams } from './rules_container'; import * as TEST_SUBJECTS from './test_subjects'; +import { useCisKubernetesIntegration } from '../../common/api/use_cis_kubernetes_integration'; jest.mock('./use_csp_integration', () => ({ useCspIntegration: jest.fn(), })); +jest.mock('../../common/api/use_cis_kubernetes_integration'); const queryClient = new QueryClient({ defaultOptions: { @@ -43,6 +45,9 @@ describe('', () => { beforeEach(() => { queryClient.clear(); jest.clearAllMocks(); + (useCisKubernetesIntegration as jest.Mock).mockImplementation(() => ({ + data: { item: { status: 'installed' } }, + })); }); it('calls API with URL params', async () => { @@ -63,6 +68,7 @@ describe('', () => { const Component = getTestComponent({ packagePolicyId: '1', policyId: '2' }); const request = { status: 'error', + isError: true, data: null, error: new Error('some error message'), }; @@ -78,6 +84,7 @@ describe('', () => { const Component = getTestComponent({ packagePolicyId: '21', policyId: '22' }); const request = { status: 'loading', + isLoading: true, }; (useCspIntegration as jest.Mock).mockReturnValue(request); diff --git a/x-pack/plugins/cloud_security_posture/public/test/fixtures/react_query.ts b/x-pack/plugins/cloud_security_posture/public/test/fixtures/react_query.ts index 2e6eeb5addb0a..201a8d85b4278 100644 --- a/x-pack/plugins/cloud_security_posture/public/test/fixtures/react_query.ts +++ b/x-pack/plugins/cloud_security_posture/public/test/fixtures/react_query.ts @@ -22,11 +22,15 @@ export const createReactQueryResponse = ({ data = undefined, }: CreateReactQueryResponseInput = {}): Partial> => { if (status === 'success') { - return { status, data }; + return { status, data, isSuccess: true, isLoading: false, isError: false }; } if (status === 'error') { - return { status, error }; + return { status, error, isSuccess: false, isLoading: false, isError: true }; + } + + if (status === 'loading') { + return { status, data: undefined, isSuccess: false, isLoading: true, isError: false }; } return { status }; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts index e414dab92606a..154c2d58cd330 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts @@ -13,7 +13,7 @@ import type { QueryDslQueryContainer, SearchRequest, } from '@elastic/elasticsearch/lib/api/types'; -import type { CloudPostureStats } from '../../../common/types'; +import type { ComplianceDashboardData } from '../../../common/types'; import { CSP_KUBEBEAT_INDEX_PATTERN, STATS_ROUTE_PATH } from '../../../common/constants'; import { CspAppContext } from '../../plugin'; import { getResourcesTypes } from './get_resources_types'; @@ -102,7 +102,7 @@ export const defineGetComplianceDashboardRoute = ( getClusters(esClient, query), ]); - const body: CloudPostureStats = { + const body: ComplianceDashboardData = { stats, resourcesTypes, clusters, diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.test.ts index df45e7fb8e737..1630d4fe3537f 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.test.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.test.ts @@ -51,7 +51,7 @@ const mockClusterBuckets: ClusterBucket[] = [ ]; describe('getClustersFromAggs', () => { - it('should return value matching CloudPostureStats["clusters"]', async () => { + it('should return value matching ComplianceDashboardData["clusters"]', async () => { const clusters = getClustersFromAggs(mockClusterBuckets); expect(clusters).toEqual([ { diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts index 04eecd67cc283..98171bf5ec332 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts @@ -11,7 +11,7 @@ import type { QueryDslQueryContainer, SearchRequest, } from '@elastic/elasticsearch/lib/api/types'; -import { CloudPostureStats } from '../../../common/types'; +import { ComplianceDashboardData } from '../../../common/types'; import { getResourceTypeFromAggs, resourceTypeAggQuery } from './get_resources_types'; import type { ResourceTypeQueryResult } from './get_resources_types'; import { CSP_KUBEBEAT_INDEX_PATTERN } from '../../../common/constants'; @@ -66,7 +66,9 @@ export const getClustersQuery = (query: QueryDslQueryContainer): SearchRequest = }, }); -export const getClustersFromAggs = (clusters: ClusterBucket[]): CloudPostureStats['clusters'] => +export const getClustersFromAggs = ( + clusters: ClusterBucket[] +): ComplianceDashboardData['clusters'] => clusters.map((cluster) => { // get cluster's meta data const benchmarks = cluster.benchmarks.buckets; @@ -101,7 +103,7 @@ export const getClustersFromAggs = (clusters: ClusterBucket[]): CloudPostureStat export const getClusters = async ( esClient: ElasticsearchClient, query: QueryDslQueryContainer -): Promise => { +): Promise => { const queryResult = await esClient.search(getClustersQuery(query), { meta: true, }); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_resources_types.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_resources_types.test.ts index b01644fc3f45b..411290738f33e 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_resources_types.test.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_resources_types.test.ts @@ -31,7 +31,7 @@ const resourceTypeBuckets: ResourceTypeBucket[] = [ ]; describe('getResourceTypeFromAggs', () => { - it('should return value matching CloudPostureStats["resourcesTypes"]', async () => { + it('should return value matching ComplianceDashboardData["resourcesTypes"]', async () => { const resourceTypes = getResourceTypeFromAggs(resourceTypeBuckets); expect(resourceTypes).toEqual([ { diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_resources_types.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_resources_types.ts index 0fc6e4b00944a..8d31fb7a8e88d 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_resources_types.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_resources_types.ts @@ -6,12 +6,12 @@ */ import { ElasticsearchClient } from 'kibana/server'; -import { +import type { AggregationsMultiBucketAggregateBase as Aggregation, QueryDslQueryContainer, SearchRequest, } from '@elastic/elasticsearch/lib/api/types'; -import { CloudPostureStats } from '../../../common/types'; +import type { ComplianceDashboardData } from '../../../common/types'; import { KeyDocCount } from './compliance_dashboard'; import { CSP_KUBEBEAT_INDEX_PATTERN } from '../../../common/constants'; @@ -53,7 +53,7 @@ export const getRisksEsQuery = (query: QueryDslQueryContainer): SearchRequest => export const getResourceTypeFromAggs = ( queryResult: ResourceTypeBucket[] -): CloudPostureStats['resourcesTypes'] => +): ComplianceDashboardData['resourcesTypes'] => queryResult.map((bucket) => ({ name: bucket.key, totalFindings: bucket.doc_count, @@ -64,7 +64,7 @@ export const getResourceTypeFromAggs = ( export const getResourcesTypes = async ( esClient: ElasticsearchClient, query: QueryDslQueryContainer -): Promise => { +): Promise => { const resourceTypesQueryResult = await esClient.search( getRisksEsQuery(query), { meta: true } diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_stats.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_stats.test.ts index 558fec85860ea..27404533afeb0 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_stats.test.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_stats.test.ts @@ -59,7 +59,7 @@ describe('getStatsFromFindingsEvaluationsAggs', () => { expect(score).toEqual(36.4); }); - it('should return value matching CloudPostureStats["stats"]', async () => { + it('should return value matching ComplianceDashboardData["stats"]', async () => { const stats = getStatsFromFindingsEvaluationsAggs(standardQueryResult); expect(stats).toEqual({ totalFailed: 30, diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_stats.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_stats.ts index 8d5417de24c52..82d1dd9a5d49f 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_stats.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_stats.ts @@ -6,9 +6,9 @@ */ import { ElasticsearchClient } from 'kibana/server'; -import { QueryDslQueryContainer, SearchRequest } from '@elastic/elasticsearch/lib/api/types'; +import type { QueryDslQueryContainer, SearchRequest } from '@elastic/elasticsearch/lib/api/types'; import { CSP_KUBEBEAT_INDEX_PATTERN } from '../../../common/constants'; -import { CloudPostureStats, Score } from '../../../common/types'; +import type { ComplianceDashboardData, Score } from '../../../common/types'; /** * @param value value is [0, 1] range @@ -44,7 +44,7 @@ export const getEvaluationsQuery = (query: QueryDslQueryContainer): SearchReques export const getStatsFromFindingsEvaluationsAggs = ( findingsEvaluationsAggs: FindingsEvaluationsQueryResult -): CloudPostureStats['stats'] => { +): ComplianceDashboardData['stats'] => { const failedFindings = findingsEvaluationsAggs.failed_findings.doc_count || 0; const passedFindings = findingsEvaluationsAggs.passed_findings.doc_count || 0; const totalFindings = failedFindings + passedFindings; @@ -62,7 +62,7 @@ export const getStatsFromFindingsEvaluationsAggs = ( export const getStats = async ( esClient: ElasticsearchClient, query: QueryDslQueryContainer -): Promise => { +): Promise => { const evaluationsQueryResult = await esClient.search( getEvaluationsQuery(query), { meta: true } From 43ad6e2597dfebe5055189c31a442f1ca11d7dd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20S=C3=A1nchez?= Date: Tue, 29 Mar 2022 11:42:32 +0200 Subject: [PATCH 10/37] [Security solution] [Endpoint] Updates translations in policy details tab for blocklist and trusted applications (#128466) * Updates translations in policy details tab for blocklist and trusted apps * Fix wrong search placeholder on blocklist list page * Fix wrong blocklist translation key * Fixes more multilangs * Adds 'entry' word next to 'blocklist' for singular reference Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../pages/blocklist/view/blocklist.tsx | 3 ++ .../view/tabs/blocklists_translations.ts | 47 ++++++++++--------- .../pages/policy/view/tabs/policy_tabs.tsx | 6 +-- .../view/tabs/trusted_apps_translations.ts | 44 ++++++++--------- 4 files changed, 52 insertions(+), 48 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/blocklist/view/blocklist.tsx b/x-pack/plugins/security_solution/public/management/pages/blocklist/view/blocklist.tsx index 75d4b22fe16a1..c4d4edb5e1331 100644 --- a/x-pack/plugins/security_solution/public/management/pages/blocklist/view/blocklist.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/blocklist/view/blocklist.tsx @@ -95,6 +95,9 @@ const BLOCKLIST_PAGE_LABELS: ArtifactListPageProps['labels'] = { 'xpack.securitySolution.blocklist.emptyStatePrimaryButtonLabel', { defaultMessage: 'Add blocklist' } ), + searchPlaceholderInfo: i18n.translate('xpack.securitySolution.blocklist.searchPlaceholderInfo', { + defaultMessage: 'Search on the fields below: name, description, value', + }), }; export const Blocklist = memo(() => { diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/tabs/blocklists_translations.ts b/x-pack/plugins/security_solution/public/management/pages/policy/view/tabs/blocklists_translations.ts index 9eb2d57a506b3..0bc32d35ea341 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/tabs/blocklists_translations.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/tabs/blocklists_translations.ts @@ -12,20 +12,20 @@ export const POLICY_ARTIFACT_BLOCKLISTS_LABELS = Object.freeze({ deleteModalTitle: i18n.translate( 'xpack.securitySolution.endpoint.policy.blocklists.list.removeDialog.title', { - defaultMessage: 'Remove blocklist from policy', + defaultMessage: 'Remove blocklist entry from policy', } ), deleteModalImpactInfo: i18n.translate( 'xpack.securitySolution.endpoint.policy.blocklists.list.removeDialog.messageCallout', { defaultMessage: - 'This blocklist will be removed only from this policy and can still be found and managed from the artifact page.', + 'This blocklist entry will be removed only from this policy and can still be found and managed from the artifact page.', } ), deleteModalErrorMessage: i18n.translate( 'xpack.securitySolution.endpoint.policy.blocklists.list.removeDialog.errorToastTitle', { - defaultMessage: 'Error while attempting to remove blocklist', + defaultMessage: 'Error while attempting to remove blocklist entry', } ), flyoutWarningCalloutMessage: (maxNumber: number) => @@ -33,37 +33,37 @@ export const POLICY_ARTIFACT_BLOCKLISTS_LABELS = Object.freeze({ 'xpack.securitySolution.endpoint.policy.blocklists.layout.flyout.searchWarning.text', { defaultMessage: - 'Only the first {maxNumber} blocklists are displayed. Please use the search bar to refine the results.', + 'Only the first {maxNumber} blocklist entries are displayed. Please use the search bar to refine the results.', values: { maxNumber }, } ), flyoutNoArtifactsToBeAssignedMessage: i18n.translate( 'xpack.securitySolution.endpoint.policy.blocklists.layout.flyout.noAssignable', { - defaultMessage: 'There are no blocklists that can be assigned to this policy.', + defaultMessage: 'There are no blocklist entries that can be assigned to this policy.', } ), flyoutTitle: i18n.translate( 'xpack.securitySolution.endpoint.policy.blocklists.layout.flyout.title', { - defaultMessage: 'Assign blocklists', + defaultMessage: 'Assign blocklist entries', } ), flyoutSubtitle: (policyName: string): string => i18n.translate('xpack.securitySolution.endpoint.policy.blocklists.layout.flyout.subtitle', { - defaultMessage: 'Select blocklists to add to {policyName}', + defaultMessage: 'Select blocklist entries to add to {policyName}', values: { policyName }, }), flyoutSearchPlaceholder: i18n.translate( 'xpack.securitySolution.endpoint.policy.blocklists.layout.search.label', { - defaultMessage: 'Search blocklists', + defaultMessage: 'Search blocklist entries', } ), flyoutErrorMessage: i18n.translate( 'xpack.securitySolution.endpoint.policy.blocklists.layout.flyout.toastError.text', { - defaultMessage: `An error occurred updating blocklists`, + defaultMessage: `An error occurred updating blocklist entry`, } ), flyoutSuccessMessageText: (updatedExceptions: ExceptionListItemSchema[]): string => @@ -71,56 +71,57 @@ export const POLICY_ARTIFACT_BLOCKLISTS_LABELS = Object.freeze({ ? i18n.translate( 'xpack.securitySolution.endpoint.policy.blocklists.layout.flyout.toastSuccess.textMultiples', { - defaultMessage: '{count} blocklists have been added to your list.', + defaultMessage: '{count} blocklist entries have been added to your list.', values: { count: updatedExceptions.length }, } ) : i18n.translate( 'xpack.securitySolution.endpoint.policy.blocklists.layout.flyout.toastSuccess.textSingle', { - defaultMessage: '"{name}" has been added to your blocklist list.', + defaultMessage: '"{name}" blocklist has been added to your list.', values: { name: updatedExceptions[0].name }, } ), emptyUnassignedTitle: i18n.translate( 'xpack.securitySolution.endpoint.policy.blocklists.empty.unassigned.title', - { defaultMessage: 'No assigned blocklists' } + { defaultMessage: 'No assigned blocklist entries' } ), emptyUnassignedMessage: (policyName: string): string => i18n.translate('xpack.securitySolution.endpoint.policy.blocklists.empty.unassigned.content', { defaultMessage: - 'There are currently no blocklists assigned to {policyName}. Assign blocklists now or add and manage them on the blocklists page.', + 'There are currently no blocklist entries assigned to {policyName}. Assign blocklist entries now or add and manage them on the blocklist page.', values: { policyName }, }), emptyUnassignedPrimaryActionButtonTitle: i18n.translate( 'xpack.securitySolution.endpoint.policy.blocklists.empty.unassigned.primaryAction', { - defaultMessage: 'Assign blocklists', + defaultMessage: 'Assign blocklist entry', } ), emptyUnassignedSecondaryActionButtonTitle: i18n.translate( 'xpack.securitySolution.endpoint.policy.blocklists.empty.unassigned.secondaryAction', { - defaultMessage: 'Manage blocklists', + defaultMessage: 'Manage blocklist entries', } ), emptyUnexistingTitle: i18n.translate( 'xpack.securitySolution.endpoint.policy.blocklists.empty.unexisting.title', - { defaultMessage: 'No blocklists exist' } + { defaultMessage: 'No blocklist entries exist' } ), emptyUnexistingMessage: i18n.translate( 'xpack.securitySolution.endpoint.policy.blocklists.empty.unexisting.content', { - defaultMessage: 'There are currently no blocklists applied to your endpoints.', + defaultMessage: 'There are currently no blocklist entries applied to your endpoints.', } ), emptyUnexistingPrimaryActionButtonTitle: i18n.translate( 'xpack.securitySolution.endpoint.policy.blocklists.empty.unexisting.action', - { defaultMessage: 'Add blocklists' } + { defaultMessage: 'Add blocklist entry' } ), listTotalItemCountMessage: (totalItemsCount: number): string => i18n.translate('xpack.securitySolution.endpoint.policy.blocklists.list.totalItemCount', { - defaultMessage: 'Showing {totalItemsCount, plural, one {# blocklist} other {# blocklists}}', + defaultMessage: + 'Showing {totalItemsCount, plural, one {# blocklist entry} other {# blocklist entries}}', values: { totalItemsCount }, }), listRemoveActionNotAllowedMessage: i18n.translate( @@ -132,22 +133,22 @@ export const POLICY_ARTIFACT_BLOCKLISTS_LABELS = Object.freeze({ listSearchPlaceholderMessage: i18n.translate( 'xpack.securitySolution.endpoint.policy.blocklists.list.search.placeholder', { - defaultMessage: `Search on the fields below: name, description, IP`, + defaultMessage: `Search on the fields below: name, description, value`, } ), layoutTitle: i18n.translate('xpack.securitySolution.endpoint.policy.blocklists.layout.title', { - defaultMessage: 'Assigned blocklists', + defaultMessage: 'Assigned blocklist entries', }), layoutAssignButtonTitle: i18n.translate( 'xpack.securitySolution.endpoint.policy.blocklists.layout.assignToPolicy', { - defaultMessage: 'Assign blocklists to policy', + defaultMessage: 'Assign blocklist entry to policy', } ), layoutViewAllLinkMessage: i18n.translate( 'xpack.securitySolution.endpoint.policy.blocklists.layout.about.viewAllLinkLabel', { - defaultMessage: 'view all blocklists', + defaultMessage: 'view all blocklist entries', } ), }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/tabs/policy_tabs.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/tabs/policy_tabs.tsx index 17c880ffa6261..6b40477c7bb6f 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/tabs/policy_tabs.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/tabs/policy_tabs.tsx @@ -123,7 +123,7 @@ export const PolicyTabs = React.memo(() => { layoutAboutMessage: (count: number, link: React.ReactElement): React.ReactNode => ( ), @@ -156,7 +156,7 @@ export const PolicyTabs = React.memo(() => { layoutAboutMessage: (count: number, link: React.ReactElement): React.ReactNode => ( ), @@ -241,7 +241,7 @@ export const PolicyTabs = React.memo(() => { [PolicyTabKeys.BLOCKLISTS]: { id: PolicyTabKeys.BLOCKLISTS, name: i18n.translate('xpack.securitySolution.endpoint.policy.details.tabs.blocklists', { - defaultMessage: 'Blocklists', + defaultMessage: 'Blocklist', }), content: ( <> diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/tabs/trusted_apps_translations.ts b/x-pack/plugins/security_solution/public/management/pages/policy/view/tabs/trusted_apps_translations.ts index f83568498df25..b990c2f9fc26c 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/tabs/trusted_apps_translations.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/tabs/trusted_apps_translations.ts @@ -12,14 +12,14 @@ export const POLICY_ARTIFACT_TRUSTED_APPS_LABELS = Object.freeze({ deleteModalTitle: i18n.translate( 'xpack.securitySolution.endpoint.policy.trustedApps.list.removeDialog.title', { - defaultMessage: 'Remove trusted app from policy', + defaultMessage: 'Remove trusted application from policy', } ), deleteModalImpactInfo: i18n.translate( 'xpack.securitySolution.endpoint.policy.trustedApps.list.removeDialog.messageCallout', { defaultMessage: - 'This trusted app will be removed only from this policy and can still be found and managed from the artifact page.', + 'This trusted application will be removed only from this policy and can still be found and managed from the artifact page.', } ), deleteModalErrorMessage: i18n.translate( @@ -33,37 +33,37 @@ export const POLICY_ARTIFACT_TRUSTED_APPS_LABELS = Object.freeze({ 'xpack.securitySolution.endpoint.policy.trustedApps.layout.flyout.searchWarning.text', { defaultMessage: - 'Only the first {maxNumber} trusted apps are displayed. Please use the search bar to refine the results.', + 'Only the first {maxNumber} trusted applications are displayed. Please use the search bar to refine the results.', values: { maxNumber }, } ), flyoutNoArtifactsToBeAssignedMessage: i18n.translate( 'xpack.securitySolution.endpoint.policy.trustedApps.layout.flyout.noAssignable', { - defaultMessage: 'There are no trusted apps that can be assigned to this policy.', + defaultMessage: 'There are no trusted applications that can be assigned to this policy.', } ), flyoutTitle: i18n.translate( 'xpack.securitySolution.endpoint.policy.trustedApps.layout.flyout.title', { - defaultMessage: 'Assign trusted apps', + defaultMessage: 'Assign trusted applications', } ), flyoutSubtitle: (policyName: string): string => i18n.translate('xpack.securitySolution.endpoint.policy.trustedApps.layout.flyout.subtitle', { - defaultMessage: 'Select trusted apps to add to {policyName}', + defaultMessage: 'Select trusted applications to add to {policyName}', values: { policyName }, }), flyoutSearchPlaceholder: i18n.translate( 'xpack.securitySolution.endpoint.policy.trustedApps.layout.search.label', { - defaultMessage: 'Search trusted apps', + defaultMessage: 'Search trusted applications', } ), flyoutErrorMessage: i18n.translate( 'xpack.securitySolution.endpoint.policy.trustedApps.layout.flyout.toastError.text', { - defaultMessage: `An error occurred updating trusted apps`, + defaultMessage: `An error occurred updating trusted applications`, } ), flyoutSuccessMessageText: (updatedExceptions: ExceptionListItemSchema[]): string => @@ -71,61 +71,61 @@ export const POLICY_ARTIFACT_TRUSTED_APPS_LABELS = Object.freeze({ ? i18n.translate( 'xpack.securitySolution.endpoint.policy.trustedApps.layout.flyout.toastSuccess.textMultiples', { - defaultMessage: '{count} trusted apps have been added to your list.', + defaultMessage: '{count} trusted applications have been added to your list.', values: { count: updatedExceptions.length }, } ) : i18n.translate( 'xpack.securitySolution.endpoint.policy.trustedApps.layout.flyout.toastSuccess.textSingle', { - defaultMessage: '"{name}" has been added to your trusted app list.', + defaultMessage: '"{name}" has been added to your trusted application list.', values: { name: updatedExceptions[0].name }, } ), emptyUnassignedTitle: i18n.translate( 'xpack.securitySolution.endpoint.policy.trustedApps.empty.unassigned.title', - { defaultMessage: 'No assigned trusted apps' } + { defaultMessage: 'No assigned trusted applications' } ), emptyUnassignedMessage: (policyName: string): string => i18n.translate('xpack.securitySolution.endpoint.policy.trustedApps.empty.unassigned.content', { defaultMessage: - 'There are currently no trusted apps assigned to {policyName}. Assign trusted apps now or add and manage them on the trusted apps page.', + 'There are currently no trusted applications assigned to {policyName}. Assign trusted applications now or add and manage them on the trusted applications page.', values: { policyName }, }), emptyUnassignedPrimaryActionButtonTitle: i18n.translate( 'xpack.securitySolution.endpoint.policy.trustedApps.empty.unassigned.primaryAction', { - defaultMessage: 'Assign trusted apps', + defaultMessage: 'Assign trusted applications', } ), emptyUnassignedSecondaryActionButtonTitle: i18n.translate( 'xpack.securitySolution.endpoint.policy.trustedApps.empty.unassigned.secondaryAction', { - defaultMessage: 'Manage trusted apps', + defaultMessage: 'Manage trusted applications', } ), emptyUnexistingTitle: i18n.translate( 'xpack.securitySolution.endpoint.policy.trustedApps.empty.unexisting.title', - { defaultMessage: 'No trusted apps exist' } + { defaultMessage: 'No trusted applications exist' } ), emptyUnexistingMessage: i18n.translate( 'xpack.securitySolution.endpoint.policy.trustedApps.empty.unexisting.content', - { defaultMessage: 'There are currently no trusted apps applied to your endpoints.' } + { defaultMessage: 'There are currently no trusted applications applied to your endpoints.' } ), emptyUnexistingPrimaryActionButtonTitle: i18n.translate( 'xpack.securitySolution.endpoint.policy.trustedApps.empty.unexisting.action', - { defaultMessage: 'Add trusted apps' } + { defaultMessage: 'Add trusted applications' } ), listTotalItemCountMessage: (totalItemsCount: number): string => i18n.translate('xpack.securitySolution.endpoint.policy.trustedApps.list.totalItemCount', { defaultMessage: - 'Showing {totalItemsCount, plural, one {# trusted app} other {# trusted apps}}', + 'Showing {totalItemsCount, plural, one {# trusted app} other {# trusted applications}}', values: { totalItemsCount }, }), listRemoveActionNotAllowedMessage: i18n.translate( 'xpack.securitySolution.endpoint.policy.trustedApps.list.removeActionNotAllowed', { - defaultMessage: 'Globally applied trusted app cannot be removed from policy.', + defaultMessage: 'Globally applied trusted application cannot be removed from policy.', } ), listSearchPlaceholderMessage: i18n.translate( @@ -135,18 +135,18 @@ export const POLICY_ARTIFACT_TRUSTED_APPS_LABELS = Object.freeze({ } ), layoutTitle: i18n.translate('xpack.securitySolution.endpoint.policy.trustedApps.layout.title', { - defaultMessage: 'Assigned trusted apps', + defaultMessage: 'Assigned trusted applications', }), layoutAssignButtonTitle: i18n.translate( 'xpack.securitySolution.endpoint.policy.trustedApps.layout.assignToPolicy', { - defaultMessage: 'Assign trusted apps to policy', + defaultMessage: 'Assign trusted applications to policy', } ), layoutViewAllLinkMessage: i18n.translate( 'xpack.securitySolution.endpoint.policy.trustedApps.layout.about.viewAllLinkLabel', { - defaultMessage: 'view all trusted apps', + defaultMessage: 'view all trusted applications', } ), }); From bf2769d7ee5994c73532739d47ca896a319740b9 Mon Sep 17 00:00:00 2001 From: Sander Philipse <94373878+sphilipse@users.noreply.github.com> Date: Tue, 29 Mar 2022 12:31:05 +0200 Subject: [PATCH 11/37] [Workplace Search] Add source skips config step correctly (#128715) --- .../components/add_source/add_source.test.tsx | 5 ++++- .../content_sources/components/add_source/add_source.tsx | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.test.tsx index e9cbafd01003e..48e57d92272ba 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.test.tsx @@ -81,7 +81,10 @@ describe('AddSourceList', () => { configured: true, }; shallow(); - expect(initializeAddSource).toHaveBeenCalledWith(expect.objectContaining({ connect: true })); + expect(initializeAddSource).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ connect: true }) + ); }); it('renders default state correctly when there are multiple connector options', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.tsx index 3fa316333bccc..98e9f32bb33ac 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.tsx @@ -59,9 +59,9 @@ export const AddSource: React.FC = (props) => { // We can land on this page from a choice page for multiple types of connectors // If that's the case we want to skip the intro and configuration, if the external & internal connector have already been configured const goToConnect = externalConnectorAvailable && externalConfigured && configured; - initializeAddSource(goToConnect ? props : { ...props, connect: true }); + initializeAddSource(goToConnect ? { ...props, connect: true } : props); return resetSourceState; - }, []); + }, [configured]); const goToConfigurationIntro = () => setAddSourceStep(AddSourceSteps.ConfigIntroStep); const goToSaveConfig = () => setAddSourceStep(AddSourceSteps.SaveConfigStep); From a07c47c652ae47d6bae35ee1edb011fda4bc853b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20S=C3=A1nchez?= Date: Tue, 29 Mar 2022 12:44:31 +0200 Subject: [PATCH 12/37] Set filter to undefined when it's empty on findExceptionLists method (#128455) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../src/api/index.ts | 2 +- .../lists/public/exceptions/api.test.ts | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/packages/kbn-securitysolution-list-api/src/api/index.ts b/packages/kbn-securitysolution-list-api/src/api/index.ts index 77c50fb32c299..a0361d044977c 100644 --- a/packages/kbn-securitysolution-list-api/src/api/index.ts +++ b/packages/kbn-securitysolution-list-api/src/api/index.ts @@ -231,7 +231,7 @@ const fetchExceptionLists = async ({ signal, }: ApiCallFetchExceptionListsProps): Promise => { const query = { - filter: filters, + filter: filters || undefined, namespace_type: namespaceTypes, page: pagination.page ? `${pagination.page}` : '1', per_page: pagination.perPage ? `${pagination.perPage}` : '20', diff --git a/x-pack/plugins/lists/public/exceptions/api.test.ts b/x-pack/plugins/lists/public/exceptions/api.test.ts index 65c11bfc1dfd0..923f4db19f62e 100644 --- a/x-pack/plugins/lists/public/exceptions/api.test.ts +++ b/x-pack/plugins/lists/public/exceptions/api.test.ts @@ -278,6 +278,31 @@ describe('Exceptions Lists API', () => { expect(exceptionResponse.data).toEqual([getExceptionListSchemaMock()]); }); + test('it returns expected exception lists when empty filter', async () => { + const exceptionResponse = await fetchExceptionLists({ + filters: '', + http: httpMock, + namespaceTypes: 'single,agnostic', + pagination: { + page: 1, + perPage: 20, + }, + signal: abortCtrl.signal, + }); + expect(httpMock.fetch).toHaveBeenCalledWith('/api/exception_lists/_find', { + method: 'GET', + query: { + namespace_type: 'single,agnostic', + page: '1', + per_page: '20', + sort_field: 'exception-list.created_at', + sort_order: 'desc', + }, + signal: abortCtrl.signal, + }); + expect(exceptionResponse.data).toEqual([getExceptionListSchemaMock()]); + }); + test('it returns error if response payload fails decode', async () => { const badPayload = getExceptionListSchemaMock(); // @ts-expect-error From 131a2892fb22b4a711155bb822408653e5ed0f0c Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 29 Mar 2022 12:49:51 +0200 Subject: [PATCH 13/37] [Lens] Add normalize_by_unit function and option (#128303) * add time scale function and option * add example to documentation * fixes * rename * fix test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/lens/public/index.ts | 1 + .../definitions/calculations/index.ts | 2 + .../definitions/calculations/time_scale.tsx | 147 ++++++++++++++++++ .../definitions/formula/formula.test.tsx | 1 + .../definitions/formula/formula.tsx | 3 +- .../operations/definitions/index.ts | 4 + .../operations/index.ts | 1 + .../operations/operations.test.ts | 4 + .../public/indexpattern_datasource/types.ts | 1 + 9 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/time_scale.tsx diff --git a/x-pack/plugins/lens/public/index.ts b/x-pack/plugins/lens/public/index.ts index 5b1501410df26..4cdbe464bd302 100644 --- a/x-pack/plugins/lens/public/index.ts +++ b/x-pack/plugins/lens/public/index.ts @@ -66,6 +66,7 @@ export type { OverallSumIndexPatternColumn, FormulaPublicApi, StaticValueIndexPatternColumn, + TimeScaleIndexPatternColumn, } from './indexpattern_datasource/types'; export type { XYArgs, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/index.ts index 1ffbdea00b775..3a3de198d4205 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/index.ts @@ -25,3 +25,5 @@ export { overallMaxOperation, overallAverageOperation, } from './overall_metric'; +export type { TimeScaleIndexPatternColumn } from './time_scale'; +export { timeScaleOperation } from './time_scale'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/time_scale.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/time_scale.tsx new file mode 100644 index 0000000000000..326d564ef4832 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/time_scale.tsx @@ -0,0 +1,147 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import type { + FormattedIndexPatternColumn, + ReferenceBasedIndexPatternColumn, +} from '../column_types'; +import { getErrorsForDateReference } from './utils'; +import type { OperationDefinition } from '..'; +import { combineErrorMessages, getFormatFromPreviousColumn } from '../helpers'; +import { IndexPatternLayer } from '../../../types'; +import { getDisallowedPreviousShiftMessage } from '../../../time_shift_utils'; + +type OverallMetricIndexPatternColumn = FormattedIndexPatternColumn & + ReferenceBasedIndexPatternColumn & { + operationType: T; + }; + +export type OverallSumIndexPatternColumn = OverallMetricIndexPatternColumn<'overall_sum'>; +export type OverallMinIndexPatternColumn = OverallMetricIndexPatternColumn<'overall_min'>; +export type OverallMaxIndexPatternColumn = OverallMetricIndexPatternColumn<'overall_max'>; +export type OverallAverageIndexPatternColumn = OverallMetricIndexPatternColumn<'overall_average'>; + +export type TimeScaleIndexPatternColumn = FormattedIndexPatternColumn & + ReferenceBasedIndexPatternColumn & { + operationType: 'normalize_by_unit'; + params: { + unit?: string; + }; + }; + +export const timeScaleOperation: OperationDefinition = + { + type: 'normalize_by_unit', + priority: 1, + displayName: i18n.translate('xpack.lens.indexPattern.timeScale', { + defaultMessage: 'Normalize by unit', + }), + input: 'fullReference', + selectionStyle: 'hidden', + requiredReferences: [ + { + input: ['field', 'managedReference', 'fullReference'], + validateMetadata: (meta) => meta.dataType === 'number' && !meta.isBucketed, + }, + ], + operationParams: [{ name: 'unit', type: 'string', required: true }], + getPossibleOperation: () => { + return { + dataType: 'number', + isBucketed: false, + scale: 'ratio', + }; + }, + getDefaultLabel: (column, indexPattern, columns) => { + return 'normalize_by_unit'; + }, + toExpression: (layer, columnId) => { + const currentColumn = layer.columns[columnId] as unknown as TimeScaleIndexPatternColumn; + const buckets = layer.columnOrder.filter((colId) => layer.columns[colId].isBucketed); + const dateColumn = buckets.find( + (colId) => layer.columns[colId].operationType === 'date_histogram' + )!; + return [ + { + type: 'function', + function: 'lens_time_scale', + arguments: { + dateColumnId: [dateColumn], + inputColumnId: [currentColumn.references[0]], + outputColumnId: [columnId], + outputColumnName: [currentColumn.label], + targetUnit: [currentColumn.params.unit!], + }, + }, + ]; + }, + buildColumn: ({ referenceIds, previousColumn, layer, indexPattern }, columnParams) => { + return { + label: 'Normalize by unit', + dataType: 'number', + operationType: 'normalize_by_unit', + isBucketed: false, + scale: 'ratio', + references: referenceIds, + params: { + ...getFormatFromPreviousColumn(previousColumn), + unit: columnParams?.unit, + }, + }; + }, + isTransferable: () => { + return true; + }, + getErrorMessage: (layer: IndexPatternLayer, columnId: string) => { + return combineErrorMessages([ + getErrorsForDateReference( + layer, + columnId, + i18n.translate('xpack.lens.indexPattern.timeScale', { + defaultMessage: 'Normalize by unit', + }) + ), + getDisallowedPreviousShiftMessage(layer, columnId), + !(layer.columns[columnId] as TimeScaleIndexPatternColumn).params.unit + ? [ + i18n.translate('xpack.lens.indexPattern.timeScale.missingUnit', { + defaultMessage: 'No unit specified for normalize by unit.', + }), + ] + : [], + ['s', 'm', 'h', 'd'].indexOf( + (layer.columns[columnId] as TimeScaleIndexPatternColumn).params.unit || 's' + ) === -1 + ? [ + i18n.translate('xpack.lens.indexPattern.timeScale.wrongUnit', { + defaultMessage: 'Unknown unit specified, use s,m,h or d.', + }), + ] + : [], + ]); + }, + filterable: false, + shiftable: false, + documentation: { + section: 'calculation', + signature: i18n.translate('xpack.lens.indexPattern.time_scale', { + defaultMessage: 'metric: number, unit: s|m|h|d|w|M|y', + }), + description: i18n.translate('xpack.lens.indexPattern.time_scale.documentation.markdown', { + defaultMessage: ` + +This advanced function is useful for normalizing counts and sums to a specific time interval. It allows for integration with metrics that are stored already normalized to a specific time interval. + +This function can only be used if there's a date histogram function used in the current chart. + +Example: A ratio comparing an already normalized metric to another metric that needs to be normalized. +\`normalize_by_unit(counter_rate(max(system.diskio.write.bytes)), unit='s') / last_value(apache.status.bytes_per_second)\` + `, + }), + }, + }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.test.tsx index 0fb7f35beb02b..51e7d363ac39d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.test.tsx @@ -343,6 +343,7 @@ describe('formula', () => { formula: 'moving_average(average(bytes), window=3)', }, references: [], + timeScale: 'd', }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.tsx index 9bbd1588d29b6..1f0fbceff3d5b 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.tsx @@ -168,6 +168,7 @@ export const formulaOperation: OperationDefinition { @@ -181,6 +182,6 @@ export const formulaOperation: OperationDefinition { "operationType": "overall_average", "type": "fullReference", }, + Object { + "operationType": "normalize_by_unit", + "type": "fullReference", + }, Object { "field": "bytes", "operationType": "min", diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/types.ts b/x-pack/plugins/lens/public/indexpattern_datasource/types.ts index 5bb4d58ed20fa..5d518b4048ed0 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/types.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/types.ts @@ -37,6 +37,7 @@ export type { MathIndexPatternColumn, OverallSumIndexPatternColumn, StaticValueIndexPatternColumn, + TimeScaleIndexPatternColumn, } from './operations'; export type { FormulaPublicApi } from './operations/definitions/formula/formula_public_api'; From 53dde5f44e77300169eab7d34aa06f3f1b446250 Mon Sep 17 00:00:00 2001 From: Pablo Machado Date: Tue, 29 Mar 2022 12:51:12 +0200 Subject: [PATCH 14/37] [Security Solution] Update user page deep links (#128722) * Update user page deep links * Fix a couple of code style issues introduced on #128375 https://github.com/elastic/kibana/pull/128375 --- .../security_solution/common/constants.ts | 7 +- .../public/app/deep_links/index.test.ts | 8 ++ .../public/app/deep_links/index.ts | 45 ++++++++++- .../users/components/all_users/index.tsx | 76 +++++++++---------- .../factory/users/all/query.all_users.dsl.ts | 4 +- 5 files changed, 93 insertions(+), 47 deletions(-) diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 2e64c710aa41c..591c7d68e17cb 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -79,7 +79,6 @@ export const DEFAULT_THREAT_MATCH_QUERY = '@timestamp >= "now-30d/d"' as const; export enum SecurityPageName { administration = 'administration', alerts = 'alerts', - authentications = 'authentications', /* * Warning: Computed values are not permitted in an enum with string valued members * The 3 following Cases page names must match `CasesDeepLinkId` in x-pack/plugins/cases/public/common/navigation.ts @@ -92,7 +91,6 @@ export enum SecurityPageName { detectionAndResponse = 'detection_response', endpoints = 'endpoints', eventFilters = 'event_filters', - events = 'events', exceptions = 'exceptions', explore = 'explore', hostIsolationExceptions = 'host_isolation_exceptions', @@ -100,6 +98,8 @@ export enum SecurityPageName { hostsAnomalies = 'hosts-anomalies', hostsExternalAlerts = 'hosts-external_alerts', hostsRisk = 'hosts-risk', + hostsEvents = 'hosts-events', + hostsAuthentications = 'hosts-authentications', investigate = 'investigate', landing = 'get_started', network = 'network', @@ -116,9 +116,12 @@ export enum SecurityPageName { trustedApps = 'trusted_apps', uncommonProcesses = 'uncommon_processes', users = 'users', + usersAuthentications = 'users-authentications', usersAnomalies = 'users-anomalies', usersRisk = 'users-risk', sessions = 'sessions', + usersEvents = 'users-events', + usersExternalAlerts = 'users-external_alerts', } export const TIMELINES_PATH = '/timelines' as const; diff --git a/x-pack/plugins/security_solution/public/app/deep_links/index.test.ts b/x-pack/plugins/security_solution/public/app/deep_links/index.test.ts index 583f1f3ea9b0d..0676c516ede64 100644 --- a/x-pack/plugins/security_solution/public/app/deep_links/index.test.ts +++ b/x-pack/plugins/security_solution/public/app/deep_links/index.test.ts @@ -154,6 +154,14 @@ describe('deepLinks', () => { expect(findDeepLink(SecurityPageName.users, deepLinks)).toBeTruthy(); }); + it('should NOT return host authentications when enableExperimental.usersEnabled === true', () => { + const deepLinks = getDeepLinks({ + ...mockGlobalState.app.enableExperimental, + usersEnabled: true, + }); + expect(findDeepLink(SecurityPageName.hostsAuthentications, deepLinks)).toBeFalsy(); + }); + it('should return NO detection & Response link when enableExperimental.detectionResponseEnabled === false', () => { const deepLinks = getDeepLinks(mockGlobalState.app.enableExperimental); expect(findDeepLink(SecurityPageName.detectionAndResponse, deepLinks)).toBeFalsy(); diff --git a/x-pack/plugins/security_solution/public/app/deep_links/index.ts b/x-pack/plugins/security_solution/public/app/deep_links/index.ts index 2df243d9b2d41..6b417a984d899 100644 --- a/x-pack/plugins/security_solution/public/app/deep_links/index.ts +++ b/x-pack/plugins/security_solution/public/app/deep_links/index.ts @@ -68,7 +68,14 @@ type Feature = typeof FEATURE[keyof typeof FEATURE]; type SecuritySolutionDeepLink = AppDeepLink & { isPremium?: boolean; features?: Feature[]; + /** + * Displays deep link when feature flag is enabled. + */ experimentalKey?: keyof ExperimentalFeatures; + /** + * Hides deep link when feature flag is enabled. + */ + hideWhenExperimentalKey?: keyof ExperimentalFeatures; deepLinks?: SecuritySolutionDeepLink[]; }; @@ -186,11 +193,12 @@ export const securitySolutionsDeepLinks: SecuritySolutionDeepLink[] = [ order: 9002, deepLinks: [ { - id: SecurityPageName.authentications, + id: SecurityPageName.hostsAuthentications, title: i18n.translate('xpack.securitySolution.search.hosts.authentications', { defaultMessage: 'Authentications', }), path: `${HOSTS_PATH}/authentications`, + hideWhenExperimentalKey: 'usersEnabled', }, { id: SecurityPageName.uncommonProcesses, @@ -200,7 +208,7 @@ export const securitySolutionsDeepLinks: SecuritySolutionDeepLink[] = [ path: `${HOSTS_PATH}/uncommonProcesses`, }, { - id: SecurityPageName.events, + id: SecurityPageName.hostsEvents, title: i18n.translate('xpack.securitySolution.search.hosts.events', { defaultMessage: 'Events', }), @@ -293,6 +301,13 @@ export const securitySolutionsDeepLinks: SecuritySolutionDeepLink[] = [ ], order: 9004, deepLinks: [ + { + id: SecurityPageName.usersAuthentications, + title: i18n.translate('xpack.securitySolution.search.users.authentications', { + defaultMessage: 'Authentications', + }), + path: `${USERS_PATH}/authentications`, + }, { id: SecurityPageName.usersAnomalies, title: i18n.translate('xpack.securitySolution.search.users.anomalies', { @@ -307,7 +322,21 @@ export const securitySolutionsDeepLinks: SecuritySolutionDeepLink[] = [ defaultMessage: 'Risk', }), path: `${USERS_PATH}/userRisk`, - isPremium: true, + experimentalKey: 'riskyUsersEnabled', + }, + { + id: SecurityPageName.usersEvents, + title: i18n.translate('xpack.securitySolution.search.users.events', { + defaultMessage: 'Events', + }), + path: `${USERS_PATH}/events`, + }, + { + id: SecurityPageName.usersExternalAlerts, + title: i18n.translate('xpack.securitySolution.search.users.externalAlerts', { + defaultMessage: 'External Alerts', + }), + path: `${USERS_PATH}/externalAlerts`, }, ], }, @@ -428,13 +457,21 @@ export function getDeepLinks( ): AppDeepLink[] { const filterDeepLinks = (securityDeepLinks: SecuritySolutionDeepLink[]): AppDeepLink[] => securityDeepLinks.reduce( - (deepLinks: AppDeepLink[], { isPremium, features, experimentalKey, ...deepLink }) => { + ( + deepLinks: AppDeepLink[], + { isPremium, features, experimentalKey, hideWhenExperimentalKey, ...deepLink } + ) => { if (licenseType && isPremium && !isPremiumLicense(licenseType)) { return deepLinks; } if (experimentalKey && !enableExperimental[experimentalKey]) { return deepLinks; } + + if (hideWhenExperimentalKey && enableExperimental[hideWhenExperimentalKey]) { + return deepLinks; + } + if (capabilities != null && !hasFeaturesCapability(features, capabilities)) { return deepLinks; } diff --git a/x-pack/plugins/security_solution/public/users/components/all_users/index.tsx b/x-pack/plugins/security_solution/public/users/components/all_users/index.tsx index 15626d0650ac8..9a00a637f551d 100644 --- a/x-pack/plugins/security_solution/public/users/components/all_users/index.tsx +++ b/x-pack/plugins/security_solution/public/users/components/all_users/index.tsx @@ -58,6 +58,44 @@ const rowItems: ItemsPerRow[] = [ }, ]; +const getUsersColumns = (): UsersTableColumns => [ + { + field: 'name', + name: i18n.USER_NAME, + truncateText: false, + sortable: true, + mobileOptions: { show: true }, + render: (name) => + getRowItemDraggables({ + rowItems: [name], + attrName: 'user.name', + idPrefix: `users-table-${name}-name`, + render: (item) => , + }), + }, + { + field: 'lastSeen', + name: i18n.LAST_SEEN, + sortable: true, + truncateText: false, + mobileOptions: { show: true }, + render: (lastSeen) => , + }, + { + field: 'domain', + name: i18n.DOMAIN, + sortable: false, + truncateText: false, + mobileOptions: { show: true }, + render: (domain) => + getRowItemDraggables({ + rowItems: [domain], + attrName: 'user.domain', + idPrefix: `users-table-${domain}-domain`, + }), + }, +]; + const UsersTableComponent: React.FC = ({ users, totalCount, @@ -146,41 +184,3 @@ const UsersTableComponent: React.FC = ({ UsersTableComponent.displayName = 'UsersTableComponent'; export const UsersTable = React.memo(UsersTableComponent); - -const getUsersColumns = (): UsersTableColumns => [ - { - field: 'name', - name: i18n.USER_NAME, - truncateText: false, - sortable: true, - mobileOptions: { show: true }, - render: (name) => - getRowItemDraggables({ - rowItems: [name], - attrName: 'user.name', - idPrefix: `users-table-${name}-name`, - render: (item) => , - }), - }, - { - field: 'lastSeen', - name: i18n.LAST_SEEN, - sortable: true, - truncateText: false, - mobileOptions: { show: true }, - render: (lastSeen) => , - }, - { - field: 'domain', - name: i18n.DOMAIN, - sortable: false, - truncateText: false, - mobileOptions: { show: true }, - render: (domain) => - getRowItemDraggables({ - rowItems: [domain], - attrName: 'user.domain', - idPrefix: `users-table-${domain}-domain`, - }), - }, -]; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/query.all_users.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/query.all_users.dsl.ts index ee98c92d2ac6b..850a590fdfe41 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/query.all_users.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/query.all_users.dsl.ts @@ -37,8 +37,6 @@ export const buildUsersQuery = ({ }, ]; - const agg = { user_count: { cardinality: { field: 'user.name' } } }; - const dslQuery = { allow_no_indices: true, index: defaultIndex, @@ -47,7 +45,7 @@ export const buildUsersQuery = ({ body: { ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), aggregations: { - ...agg, + user_count: { cardinality: { field: 'user.name' } }, user_data: { terms: { size: querySize, field: 'user.name', order: getQueryOrder(sort) }, aggs: { From 17086ad6ceb726411a99c2a41477f6e89359b9a1 Mon Sep 17 00:00:00 2001 From: Georgii Gorbachev Date: Tue, 29 Mar 2022 13:42:36 +0200 Subject: [PATCH 15/37] [Security Solution][Detections] Adds UI for bulk applying timeline template (#128691) **Addresses:** https://github.com/elastic/kibana/issues/93083, https://github.com/elastic/security-team/issues/2078 (internal) ## Summary This PR adds a UI for applying a timeline template to multiple rules in bulk. - A new bulk actions menu item to the Rule Management table. - A new form flyout for applying a timeline template. - Some glue code to connect them. There are a few issues that I'd like to address in a follow-up PR after the FF: 1. Resetting already applied templates to `None` doesn't work because of the way the `patchRules` function works. This is a known bug in this implementation. We will need to replace `patchRules` with something else for bulk editing actions. 2. I need to add some test coverage. Other notes: - I changed some copies to hopefully make it a little bit clearer. Let me know if you want to rephrase. ## Screenshots ![](https://puu.sh/IRpnL/9abe2ce1b5.png) The template selector doesn't look good on a smaller screen: ![](https://puu.sh/IRpyP/eb7bebabc7.png) --- .../all/bulk_actions/bulk_edit_flyout.tsx | 4 + .../forms/bulk_edit_form_wrapper.tsx | 14 +-- .../forms/timeline_template_form.tsx | 102 ++++++++++++++++++ .../rules/all/bulk_actions/translations.tsx | 50 +++++++++ .../all/bulk_actions/use_bulk_actions.tsx | 10 ++ .../detection_engine/rules/translations.ts | 7 ++ .../rules/bulk_action_edit.ts | 11 +- 7 files changed, 187 insertions(+), 11 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/timeline_template_form.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/translations.tsx diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/bulk_edit_flyout.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/bulk_edit_flyout.tsx index a6b5f664fc7cd..07967ae53a041 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/bulk_edit_flyout.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/bulk_edit_flyout.tsx @@ -14,6 +14,7 @@ import { import { IndexPatternsForm } from './forms/index_patterns_form'; import { TagsForm } from './forms/tags_form'; +import { TimelineTemplateForm } from './forms/timeline_template_form'; interface BulkEditFlyoutProps { onClose: () => void; @@ -35,6 +36,9 @@ const BulkEditFlyoutComponent = ({ editAction, tags, ...props }: BulkEditFlyoutP case BulkActionEditType.set_tags: return ; + case BulkActionEditType.set_timeline: + return ; + default: return null; } diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/bulk_edit_form_wrapper.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/bulk_edit_form_wrapper.tsx index a56a4fe3d159e..49d4487808740 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/bulk_edit_form_wrapper.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/bulk_edit_form_wrapper.tsx @@ -24,19 +24,21 @@ import { Form, FormHook } from '../../../../../../../shared_imports'; import * as i18n from '../../../translations'; interface BulkEditFormWrapperProps { - onClose: () => void; - onSubmit: () => void; - title: string; form: FormHook; + title: string; + banner?: React.ReactNode; children: React.ReactNode; + onClose: () => void; + onSubmit: () => void; } const BulkEditFormWrapperComponent: FC = ({ form, + title, + banner, + children, onClose, onSubmit, - children, - title, }) => { const simpleFlyoutTitleId = useGeneratedHtmlId({ prefix: 'RulesBulkEditForm', @@ -50,7 +52,7 @@ const BulkEditFormWrapperComponent: FC = ({

{title}

- +
{children}
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/timeline_template_form.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/timeline_template_form.tsx new file mode 100644 index 0000000000000..8e67b2711a278 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/forms/timeline_template_form.tsx @@ -0,0 +1,102 @@ +/* + * 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 React, { useCallback } from 'react'; +import { EuiCallOut } from '@elastic/eui'; + +import { useForm, UseField, FormSchema } from '../../../../../../../shared_imports'; +import { PickTimeline } from '../../../../../../components/rules/pick_timeline'; +import { + BulkActionEditType, + BulkActionEditPayload, +} from '../../../../../../../../common/detection_engine/schemas/common/schemas'; + +import { BulkEditFormWrapper } from './bulk_edit_form_wrapper'; +import { bulkApplyTimelineTemplate as i18n } from '../translations'; + +export interface TimelineTemplateFormData { + timeline: { + id: string | null; + title: string; + }; +} + +const formSchema: FormSchema = { + timeline: { + label: i18n.TEMPLATE_SELECTOR_LABEL, + helpText: i18n.TEMPLATE_SELECTOR_HELP_TEXT, + }, +}; + +const defaultFormData: TimelineTemplateFormData = { + timeline: { + id: null, + title: i18n.TEMPLATE_SELECTOR_DEFAULT_VALUE, + }, +}; + +interface TimelineTemplateFormProps { + rulesCount: number; + onClose: () => void; + onConfirm: (bulkActionEditPayload: BulkActionEditPayload) => void; +} + +const TimelineTemplateFormComponent = (props: TimelineTemplateFormProps) => { + const { rulesCount, onClose, onConfirm } = props; + + const { form } = useForm({ + schema: formSchema, + defaultValue: defaultFormData, + }); + + const handleSubmit = useCallback(async () => { + const { data, isValid } = await form.submit(); + if (!isValid) { + return; + } + + const timelineId = data.timeline.id || ''; + const timelineTitle = timelineId ? data.timeline.title : ''; + + onConfirm({ + type: BulkActionEditType.set_timeline, + value: { + timeline_id: timelineId, + timeline_title: timelineTitle, + }, + }); + }, [form, onConfirm]); + + const warningCallout = ( + + {i18n.warningCalloutMessage(rulesCount)} + + ); + + return ( + + {/* Timeline template selector */} + + + ); +}; + +export const TimelineTemplateForm = React.memo(TimelineTemplateFormComponent); +TimelineTemplateForm.displayName = 'TimelineTemplateForm'; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/translations.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/translations.tsx new file mode 100644 index 0000000000000..d35a89484f264 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/translations.tsx @@ -0,0 +1,50 @@ +/* + * 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 React from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + +export const bulkApplyTimelineTemplate = { + FORM_TITLE: i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.applyTimelineTemplate.formTitle', + { + defaultMessage: 'Apply timeline template', + } + ), + + TEMPLATE_SELECTOR_LABEL: i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.applyTimelineTemplate.templateSelectorLabel', + { + defaultMessage: 'Apply timeline template to selected rules', + } + ), + + TEMPLATE_SELECTOR_HELP_TEXT: i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.applyTimelineTemplate.templateSelectorHelpText', + { + defaultMessage: + 'Select which timeline to apply to selected rules when investigating generated alerts.', + } + ), + + TEMPLATE_SELECTOR_DEFAULT_VALUE: i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.applyTimelineTemplate.templateSelectorDefaultValue', + { + defaultMessage: 'None', + } + ), + + warningCalloutMessage: (rulesCount: number): JSX.Element => ( + + ), +}; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_actions.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_actions.tsx index 6bd7cfc67f308..9aa175002af7b 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_actions.tsx @@ -307,6 +307,16 @@ export const useBulkActions = ({ disabled: isEditDisabled, panel: 1, }, + { + key: i18n.BULK_ACTION_APPLY_TIMELINE_TEMPLATE, + name: i18n.BULK_ACTION_APPLY_TIMELINE_TEMPLATE, + 'data-test-subj': 'applyTimelineTemplateBulk', + disabled: isEditDisabled, + onClick: handleBulkEdit(BulkActionEditType.set_timeline), + toolTipContent: missingActionPrivileges ? i18n.EDIT_RULE_SETTINGS_TOOLTIP : undefined, + toolTipPosition: 'right', + icon: undefined, + }, { key: i18n.BULK_ACTION_EXPORT, name: i18n.BULK_ACTION_EXPORT, diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts index f93ca2f48d2db..f99ebc2c72c26 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts @@ -193,6 +193,13 @@ export const BULK_ACTION_DELETE_TAGS = i18n.translate( } ); +export const BULK_ACTION_APPLY_TIMELINE_TEMPLATE = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.applyTimelineTemplateTitle', + { + defaultMessage: 'Apply timeline template', + } +); + export const BULK_ACTION_MENU_TITLE = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.contextMenuTitle', { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_action_edit.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_action_edit.ts index 8df66dcc3b191..5133d1604d621 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_action_edit.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_action_edit.ts @@ -78,11 +78,12 @@ export const applyBulkActionEditToRule = ( // timeline actions case BulkActionEditType.set_timeline: - rule.params = { - ...rule.params, - timelineId: action.value.timeline_id, - timelineTitle: action.value.timeline_title, - }; + const timelineId = action.value.timeline_id.trim() || undefined; + const timelineTitle = timelineId ? action.value.timeline_title : undefined; + + rule.params.timelineId = timelineId; + rule.params.timelineTitle = timelineTitle; + break; } return rule; From 92aea763cbc4c68bd86b601711c7569aee48f0e3 Mon Sep 17 00:00:00 2001 From: Oliver Gupte Date: Tue, 29 Mar 2022 08:10:00 -0400 Subject: [PATCH 16/37] [APM] ensure rum_allow_origins setting only saves valid YAML strings (#128703) (#128704) --- .../fleet/create_cloud_apm_package_policy.ts | 3 ++- .../get_apm_package_policy_definition.ts | 23 +++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/apm/server/routes/fleet/create_cloud_apm_package_policy.ts b/x-pack/plugins/apm/server/routes/fleet/create_cloud_apm_package_policy.ts index 797bce77facdb..670932ea6dbbd 100644 --- a/x-pack/plugins/apm/server/routes/fleet/create_cloud_apm_package_policy.ts +++ b/x-pack/plugins/apm/server/routes/fleet/create_cloud_apm_package_policy.ts @@ -23,6 +23,7 @@ import { import { getApmPackagePolicyDefinition } from './get_apm_package_policy_definition'; import { Setup } from '../../lib/helpers/setup_request'; import { mergePackagePolicyWithApm } from './merge_package_policy_with_apm'; +import { ELASTIC_CLOUD_APM_AGENT_POLICY_ID } from '../../../common/fleet'; export async function createCloudApmPackgePolicy({ cloudPluginSetup, @@ -65,7 +66,7 @@ export async function createCloudApmPackgePolicy({ savedObjectsClient, esClient, mergedAPMPackagePolicy, - { force: true, bumpRevision: true } + { id: ELASTIC_CLOUD_APM_AGENT_POLICY_ID, force: true, bumpRevision: true } ); logger.info(`Fleet migration on Cloud - apmPackagePolicy create end`); return apmPackagePolicy; diff --git a/x-pack/plugins/apm/server/routes/fleet/get_apm_package_policy_definition.ts b/x-pack/plugins/apm/server/routes/fleet/get_apm_package_policy_definition.ts index 85ac03697019c..28b40447136ce 100644 --- a/x-pack/plugins/apm/server/routes/fleet/get_apm_package_policy_definition.ts +++ b/x-pack/plugins/apm/server/routes/fleet/get_apm_package_policy_definition.ts @@ -5,12 +5,12 @@ * 2.0. */ +import yaml from 'js-yaml'; import { KibanaRequest } from 'kibana/server'; import { RegistryVarsEntry } from '../../../../fleet/common'; import { POLICY_ELASTIC_AGENT_ON_CLOUD, INPUT_VAR_NAME_TO_SCHEMA_PATH, - ELASTIC_CLOUD_APM_AGENT_POLICY_ID, } from '../../../common/fleet'; import { APMPluginSetupDependencies, @@ -36,7 +36,6 @@ export async function getApmPackagePolicyDefinition({ }); return { - id: ELASTIC_CLOUD_APM_AGENT_POLICY_ID, name: 'Elastic APM', namespace: 'default', enabled: true, @@ -73,6 +72,9 @@ function getApmPackageInputVars({ }): Record { const overrideValues: Record = { url: cloudPluginSetup?.apm?.url, // overrides 'apm-server.url' to be the cloud APM host + rum_allow_origins: ensureValidMultiText( + apmServerSchema[INPUT_VAR_NAME_TO_SCHEMA_PATH.rum_allow_origins] + ), // fixes issue where "*" needs to be wrapped in quotes to be parsed as a YAML string }; return policyTemplateInputVars.reduce((acc, registryVarsEntry) => { @@ -90,3 +92,20 @@ function getApmPackageInputVars({ }; }, {}); } + +function ensureValidMultiText(textMultiValue: string[] | undefined) { + if (!textMultiValue) { + return undefined; + } + return textMultiValue.map(escapeInvalidYamlString); +} +function escapeInvalidYamlString(yamlString: string) { + try { + yaml.load(yamlString); + } catch (error) { + if (error instanceof yaml.YAMLException) { + return `"${yamlString}"`; + } + } + return yamlString; +} From 99ec409646dd3878b3352b8139c3098887aa1469 Mon Sep 17 00:00:00 2001 From: Katerina Patticha Date: Tue, 29 Mar 2022 14:24:13 +0200 Subject: [PATCH 17/37] Fix synthrace command (#128729) --- x-pack/plugins/apm/dev_docs/local_setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/dev_docs/local_setup.md b/x-pack/plugins/apm/dev_docs/local_setup.md index f021f41b17c80..e41ee4390d58c 100644 --- a/x-pack/plugins/apm/dev_docs/local_setup.md +++ b/x-pack/plugins/apm/dev_docs/local_setup.md @@ -29,7 +29,7 @@ node ./scripts/es_archiver load "x-pack/plugins/apm/ftr_e2e/cypress/fixtures/es_ **Run Synthtrace** ``` -node packages/elastic-apm-synthtrace/src/scripts/run packages/elastic-apm-synthtrace/src/scripts/examples/01_simple_trace.ts --target=http://elastic:changeme@localhost:9200 +node packages/elastic-apm-synthtrace/src/scripts/run packages/elastic-apm-synthtrace/src/scripts/examples/01_simple_trace.ts --target=http://localhost:9200 --username=elastic --password=changeme ``` **Connect Kibana to ES** From 569e4e8458d91f05a9ae3c3f8192640230ac25c1 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 29 Mar 2022 13:37:53 +0100 Subject: [PATCH 18/37] skip flaky suite (#127077) --- .../server/integration_tests/cloud_preconfiguration.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/server/integration_tests/cloud_preconfiguration.test.ts b/x-pack/plugins/fleet/server/integration_tests/cloud_preconfiguration.test.ts index b55a95aec6103..5fa3bb118b0ed 100644 --- a/x-pack/plugins/fleet/server/integration_tests/cloud_preconfiguration.test.ts +++ b/x-pack/plugins/fleet/server/integration_tests/cloud_preconfiguration.test.ts @@ -108,7 +108,8 @@ describe.skip('Fleet preconfiguration reset', () => { await stopServers(); }); - describe('Preconfigred cloud policy', () => { + // FLAKY: https://github.com/elastic/kibana/issues/127077 + describe.skip('Preconfigred cloud policy', () => { it('Works and preconfigure correctly agent policies', async () => { const agentPolicies = await kbnServer.coreStart.savedObjects .createInternalRepository() From 91394d41e7d9cbe2eefa242aeb21c8523a369111 Mon Sep 17 00:00:00 2001 From: Dmitry Tomashevich <39378793+Dmitriynj@users.noreply.github.com> Date: Tue, 29 Mar 2022 17:48:49 +0500 Subject: [PATCH 19/37] [Graph] Clean up deprecated APIs (#128579) * [Discover] clean up deprecated apis * [Graph] apply suggestions * [Discover] replace the last DataView occurrence Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/graph/public/application.tsx | 4 ++-- .../graph/public/components/inspect_panel.tsx | 4 ++-- .../graph/public/components/search_bar.test.tsx | 7 ++++--- .../graph/public/components/search_bar.tsx | 15 ++++++--------- .../workspace_layout/workspace_layout.tsx | 6 +++--- .../graph/public/services/index_pattern_cache.ts | 6 +++--- .../services/persistence/deserialize.test.ts | 6 +++--- .../public/services/persistence/deserialize.ts | 15 +++++---------- .../public/state_management/datasource.sagas.ts | 4 ++-- .../public/state_management/datasource.test.ts | 4 ++-- .../graph/public/state_management/mocks.ts | 4 ++-- .../graph/public/state_management/persistence.ts | 4 ++-- x-pack/plugins/graph/public/types/app_state.ts | 4 ++-- 13 files changed, 38 insertions(+), 45 deletions(-) diff --git a/x-pack/plugins/graph/public/application.tsx b/x-pack/plugins/graph/public/application.tsx index 235c9aa843797..adc396965f6a7 100644 --- a/x-pack/plugins/graph/public/application.tsx +++ b/x-pack/plugins/graph/public/application.tsx @@ -21,7 +21,7 @@ import { } from 'kibana/public'; import ReactDOM from 'react-dom'; import React from 'react'; -import { DataPlugin, IndexPatternsContract } from '../../../../src/plugins/data/public'; +import { DataPlugin, DataViewsContract } from '../../../../src/plugins/data/public'; import { LicensingPluginStart } from '../../licensing/public'; import { checkLicense } from '../common/check_license'; import { NavigationPublicPluginStart as NavigationStart } from '../../../../src/plugins/navigation/public'; @@ -52,7 +52,7 @@ export interface GraphDependencies { licensing: LicensingPluginStart; chrome: ChromeStart; toastNotifications: ToastsStart; - indexPatterns: IndexPatternsContract; + indexPatterns: DataViewsContract; data: ReturnType; savedObjectsClient: SavedObjectsClientContract; addBasePath: (url: string) => string; diff --git a/x-pack/plugins/graph/public/components/inspect_panel.tsx b/x-pack/plugins/graph/public/components/inspect_panel.tsx index 5a0ad8b5f291c..6881a750f76ed 100644 --- a/x-pack/plugins/graph/public/components/inspect_panel.tsx +++ b/x-pack/plugins/graph/public/components/inspect_panel.tsx @@ -9,12 +9,12 @@ import React, { useMemo, useState } from 'react'; import { EuiTab, EuiTabs, EuiText } from '@elastic/eui'; import { monaco, XJsonLang } from '@kbn/monaco'; import { FormattedMessage } from '@kbn/i18n-react'; -import { IndexPattern } from '../../../../../src/plugins/data/public'; import { CodeEditor } from '../../../../../src/plugins/kibana_react/public'; +import type { DataView } from '../../../../../src/plugins/data_views/public'; interface InspectPanelProps { showInspect: boolean; - indexPattern?: IndexPattern; + indexPattern?: DataView; lastRequest?: string; lastResponse?: string; } diff --git a/x-pack/plugins/graph/public/components/search_bar.test.tsx b/x-pack/plugins/graph/public/components/search_bar.test.tsx index 72f95ecb0dc10..7feab56b2e272 100644 --- a/x-pack/plugins/graph/public/components/search_bar.test.tsx +++ b/x-pack/plugins/graph/public/components/search_bar.test.tsx @@ -17,7 +17,8 @@ import { SavedObjectsStart, } from 'kibana/public'; import { act } from 'react-dom/test-utils'; -import { IndexPattern, QueryStringInput } from '../../../../../src/plugins/data/public'; +import { QueryStringInput } from '../../../../../src/plugins/data/public'; +import type { DataView } from '../../../../../src/plugins/data_views/public'; import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; import { I18nProvider, InjectedIntl } from '@kbn/i18n-react'; @@ -87,12 +88,12 @@ describe('search_bar', () => { const defaultProps = { isLoading: false, indexPatternProvider: { - get: jest.fn(() => Promise.resolve({ fields: [] } as unknown as IndexPattern)), + get: jest.fn(() => Promise.resolve({ fields: [] } as unknown as DataView)), }, confirmWipeWorkspace: (callback: () => void) => { callback(); }, - onIndexPatternChange: (indexPattern?: IndexPattern) => { + onIndexPatternChange: (indexPattern?: DataView) => { instance.setProps({ ...defaultProps, currentIndexPattern: indexPattern, diff --git a/x-pack/plugins/graph/public/components/search_bar.tsx b/x-pack/plugins/graph/public/components/search_bar.tsx index 0760fb4fd2c13..89a82b5eb8483 100644 --- a/x-pack/plugins/graph/public/components/search_bar.tsx +++ b/x-pack/plugins/graph/public/components/search_bar.tsx @@ -10,6 +10,7 @@ import React, { useState, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { connect } from 'react-redux'; +import { toElasticsearchQuery, fromKueryExpression } from '@kbn/es-query'; import { IndexPatternSavedObject, IndexPatternProvider, WorkspaceField } from '../types'; import { openSourceModal } from '../services/source_modal'; import { @@ -23,19 +24,18 @@ import { import { useKibana } from '../../../../../src/plugins/kibana_react/public'; import { - IndexPattern, QueryStringInput, IDataPluginServices, Query, - esKuery, } from '../../../../../src/plugins/data/public'; import { TooltipWrapper } from './tooltip_wrapper'; +import type { DataView } from '../../../../../src/plugins/data_views/public'; export interface SearchBarProps { isLoading: boolean; urlQuery: string | null; - currentIndexPattern?: IndexPattern; - onIndexPatternChange: (indexPattern?: IndexPattern) => void; + currentIndexPattern?: DataView; + onIndexPatternChange: (indexPattern?: DataView) => void; confirmWipeWorkspace: ( onConfirm: () => void, text?: string, @@ -51,12 +51,9 @@ export interface SearchBarStateProps { submit: (searchTerm: string) => void; } -function queryToString(query: Query, indexPattern: IndexPattern) { +function queryToString(query: Query, indexPattern: DataView) { if (query.language === 'kuery' && typeof query.query === 'string') { - const dsl = esKuery.toElasticsearchQuery( - esKuery.fromKueryExpression(query.query as string), - indexPattern - ); + const dsl = toElasticsearchQuery(fromKueryExpression(query.query as string), indexPattern); // JSON representation of query will be handled by existing logic. // TODO clean this up and handle it in the data fetch layer once // it moved to typescript. diff --git a/x-pack/plugins/graph/public/components/workspace_layout/workspace_layout.tsx b/x-pack/plugins/graph/public/components/workspace_layout/workspace_layout.tsx index 5426ae9228518..3e7ff634eef0e 100644 --- a/x-pack/plugins/graph/public/components/workspace_layout/workspace_layout.tsx +++ b/x-pack/plugins/graph/public/components/workspace_layout/workspace_layout.tsx @@ -17,7 +17,7 @@ import { workspaceInitializedSelector, } from '../../state_management'; import { FieldManager } from '../field_manager'; -import { IndexPattern } from '../../../../../../src/plugins/data/public'; +import type { DataView } from '../../../../../../src/plugins/data_views/public'; import { ControlType, IndexPatternProvider, @@ -89,7 +89,7 @@ export const WorkspaceLayoutComponent = ({ sharingSavedObjectProps, spaces, }: WorkspaceLayoutProps & WorkspaceLayoutStateProps) => { - const [currentIndexPattern, setCurrentIndexPattern] = useState(); + const [currentIndexPattern, setCurrentIndexPattern] = useState(); const [showInspect, setShowInspect] = useState(false); const [pickerOpen, setPickerOpen] = useState(false); const [mergeCandidates, setMergeCandidates] = useState([]); @@ -112,7 +112,7 @@ export const WorkspaceLayoutComponent = ({ }, []); const onIndexPatternChange = useCallback( - (indexPattern?: IndexPattern) => setCurrentIndexPattern(indexPattern), + (indexPattern?: DataView) => setCurrentIndexPattern(indexPattern), [] ); diff --git a/x-pack/plugins/graph/public/services/index_pattern_cache.ts b/x-pack/plugins/graph/public/services/index_pattern_cache.ts index ddf630388a4f3..d6156bc9d0626 100644 --- a/x-pack/plugins/graph/public/services/index_pattern_cache.ts +++ b/x-pack/plugins/graph/public/services/index_pattern_cache.ts @@ -6,12 +6,12 @@ */ import { IndexPatternProvider } from '../types'; -import { IndexPattern } from '../../../../../src/plugins/data/public'; +import type { DataView } from '../../../../../src/plugins/data_views/public'; export function createCachedIndexPatternProvider( - indexPatternGetter: (id: string) => Promise + indexPatternGetter: (id: string) => Promise ): IndexPatternProvider { - const cache = new Map(); + const cache = new Map(); return { get: async (id: string) => { diff --git a/x-pack/plugins/graph/public/services/persistence/deserialize.test.ts b/x-pack/plugins/graph/public/services/persistence/deserialize.test.ts index 31826c3b3a747..cc180065d3aca 100644 --- a/x-pack/plugins/graph/public/services/persistence/deserialize.test.ts +++ b/x-pack/plugins/graph/public/services/persistence/deserialize.test.ts @@ -9,7 +9,7 @@ import { GraphWorkspaceSavedObject, IndexPatternSavedObject, Workspace } from '. import { migrateLegacyIndexPatternRef, savedWorkspaceToAppState, mapFields } from './deserialize'; import { createWorkspace } from '../../services/workspace/graph_client_workspace'; import { outlinkEncoders } from '../../helpers/outlink_encoders'; -import { IndexPattern } from '../../../../../../src/plugins/data/public'; +import type { DataView } from '../../../../../../src/plugins/data_views/public'; describe('deserialize', () => { let savedWorkspace: GraphWorkspaceSavedObject; @@ -124,7 +124,7 @@ describe('deserialize', () => { { name: 'field2', type: 'string', aggregatable: true, isMapped: true }, { name: 'field3', type: 'string', aggregatable: true, isMapped: true }, ], - } as IndexPattern, + } as DataView, workspace ); } @@ -247,7 +247,7 @@ describe('deserialize', () => { { name: 'runtimeField', type: 'string', aggregatable: true, isMapped: false }, { name: 'field3', type: 'string', aggregatable: true, isMapped: true }, ], - } as IndexPattern; + } as DataView; expect(mapFields(indexPattern).map(({ name }) => name)).toEqual([ 'field1', 'field2', diff --git a/x-pack/plugins/graph/public/services/persistence/deserialize.ts b/x-pack/plugins/graph/public/services/persistence/deserialize.ts index d76a07ab0fc32..1223c03b6f6c1 100644 --- a/x-pack/plugins/graph/public/services/persistence/deserialize.ts +++ b/x-pack/plugins/graph/public/services/persistence/deserialize.ts @@ -25,10 +25,8 @@ import { colorChoices, iconChoicesByClass, } from '../../helpers/style_choices'; -import { - IndexPattern, - indexPatterns as indexPatternsUtils, -} from '../../../../../../src/plugins/data/public'; +import { indexPatterns as indexPatternsUtils } from '../../../../../../src/plugins/data/public'; +import type { DataView } from '../../../../../../src/plugins/data_views/public'; const defaultAdvancedSettings: AdvancedSettings = { useSignificance: true, @@ -98,7 +96,7 @@ export function lookupIndexPatternId(savedWorkspace: GraphWorkspaceSavedObject) } // returns all graph fields mapped out of the index pattern -export function mapFields(indexPattern: IndexPattern): WorkspaceField[] { +export function mapFields(indexPattern: DataView): WorkspaceField[] { const blockedFieldNames = ['_id', '_index', '_score', '_source', '_type']; const defaultHopSize = 5; @@ -131,10 +129,7 @@ export function mapFields(indexPattern: IndexPattern): WorkspaceField[] { }); } -function getFieldsWithWorkspaceSettings( - indexPattern: IndexPattern, - selectedFields: SerializedField[] -) { +function getFieldsWithWorkspaceSettings(indexPattern: DataView, selectedFields: SerializedField[]) { const allFields = mapFields(indexPattern); // merge in selected information into all fields @@ -216,7 +211,7 @@ export function makeNodeId(field: string, term: string) { export function savedWorkspaceToAppState( savedWorkspace: GraphWorkspaceSavedObject, - indexPattern: IndexPattern, + indexPattern: DataView, workspaceInstance: Workspace ): { urlTemplates: UrlTemplate[]; diff --git a/x-pack/plugins/graph/public/state_management/datasource.sagas.ts b/x-pack/plugins/graph/public/state_management/datasource.sagas.ts index 55e23c5e7c7c1..bbab3cd13b7a9 100644 --- a/x-pack/plugins/graph/public/state_management/datasource.sagas.ts +++ b/x-pack/plugins/graph/public/state_management/datasource.sagas.ts @@ -18,7 +18,7 @@ import { setDatasource, requestDatasource, } from './datasource'; -import { IndexPattern } from '../../../../../src/plugins/data/public'; +import type { DataView } from '../../../../../src/plugins/data_views/public'; /** * Saga loading field information when the datasource is switched. This will overwrite current settings @@ -34,7 +34,7 @@ export const datasourceSaga = ({ }: GraphStoreDependencies) => { function* fetchFields(action: Action) { try { - const indexPattern: IndexPattern = yield call(indexPatternProvider.get, action.payload.id); + const indexPattern: DataView = yield call(indexPatternProvider.get, action.payload.id); yield put(loadFields(mapFields(indexPattern))); yield put(datasourceLoaded()); const advancedSettings = settingsSelector(yield select()); diff --git a/x-pack/plugins/graph/public/state_management/datasource.test.ts b/x-pack/plugins/graph/public/state_management/datasource.test.ts index 00efe0c154732..ab37ed76ac08b 100644 --- a/x-pack/plugins/graph/public/state_management/datasource.test.ts +++ b/x-pack/plugins/graph/public/state_management/datasource.test.ts @@ -11,7 +11,7 @@ import { datasourceSelector, requestDatasource } from './datasource'; import { datasourceSaga } from './datasource.sagas'; import { fieldsSelector } from './fields'; import { updateSettings } from './advanced_settings'; -import { IndexPattern } from '../../../../../src/plugins/data/public'; +import type { DataView } from '../../../../../src/plugins/data_views/public'; const waitForPromise = () => new Promise((r) => setTimeout(r)); @@ -27,7 +27,7 @@ describe('datasource saga', () => { Promise.resolve({ title: 'test-pattern', getNonScriptedFields: () => [{ name: 'field1', type: 'string', isMapped: true }], - } as IndexPattern) + } as DataView) ), }, }, diff --git a/x-pack/plugins/graph/public/state_management/mocks.ts b/x-pack/plugins/graph/public/state_management/mocks.ts index 906bcde9070fc..06f9fe52f936a 100644 --- a/x-pack/plugins/graph/public/state_management/mocks.ts +++ b/x-pack/plugins/graph/public/state_management/mocks.ts @@ -16,7 +16,7 @@ import { createStore, applyMiddleware, AnyAction } from 'redux'; import { ChromeStart } from 'kibana/public'; import { GraphStoreDependencies, createRootReducer, GraphStore, GraphState } from './store'; import { Workspace } from '../types'; -import { IndexPattern } from '../../../../../src/plugins/data/public'; +import type { DataView } from '../../../../../src/plugins/data_views/public'; export interface MockedGraphEnvironment { store: GraphStore; @@ -63,7 +63,7 @@ export function createMockGraphStore({ if (id === 'missing-dataview') { throw Error('No data view with this id'); } - return { id: '123', title: 'test-pattern' } as unknown as IndexPattern; + return { id: '123', title: 'test-pattern' } as unknown as DataView; }), }, I18nContext: jest diff --git a/x-pack/plugins/graph/public/state_management/persistence.ts b/x-pack/plugins/graph/public/state_management/persistence.ts index d1e038bbb2102..d043df2510ed6 100644 --- a/x-pack/plugins/graph/public/state_management/persistence.ts +++ b/x-pack/plugins/graph/public/state_management/persistence.ts @@ -25,7 +25,7 @@ import { updateMetaData, metaDataSelector } from './meta_data'; import { openSaveModal, SaveWorkspaceHandler } from '../services/save_modal'; import { getEditPath } from '../services/url'; import { saveSavedWorkspace } from '../helpers/saved_workspace_utils'; -import type { IndexPattern } from '../../../../../src/plugins/data/public'; +import type { DataView } from '../../../../../src/plugins/data_views/public'; export interface LoadSavedWorkspacePayload { indexPatterns: IndexPatternSavedObject[]; @@ -68,7 +68,7 @@ export const loadingSaga = ({ const selectedIndexPatternId = lookupIndexPatternId(savedWorkspace); let indexPattern; try { - indexPattern = (yield call(indexPatternProvider.get, selectedIndexPatternId)) as IndexPattern; + indexPattern = (yield call(indexPatternProvider.get, selectedIndexPatternId)) as DataView; } catch (e) { notifications.toasts.addDanger( i18n.translate('xpack.graph.loadWorkspace.missingDataViewErrorMessage', { diff --git a/x-pack/plugins/graph/public/types/app_state.ts b/x-pack/plugins/graph/public/types/app_state.ts index 1ec21c4991a1b..0d707cef9543b 100644 --- a/x-pack/plugins/graph/public/types/app_state.ts +++ b/x-pack/plugins/graph/public/types/app_state.ts @@ -8,7 +8,7 @@ import { SimpleSavedObject } from 'src/core/public'; import { FontawesomeIcon } from '../helpers/style_choices'; import { OutlinkEncoder } from '../helpers/outlink_encoders'; -import type { IndexPattern } from '../../../../../src/plugins/data/public'; +import type { DataView } from '../../../../../src/plugins/data_views/public'; export interface UrlTemplate { url: string; @@ -41,5 +41,5 @@ export interface AdvancedSettings { export type IndexPatternSavedObject = SimpleSavedObject<{ title: string }>; export interface IndexPatternProvider { - get(id: string): Promise; + get(id: string): Promise; } From bc6a6e444ff12a37b7b267e0eb1a9302d2cbafb0 Mon Sep 17 00:00:00 2001 From: Sandra G Date: Tue, 29 Mar 2022 09:04:56 -0400 Subject: [PATCH 20/37] [Stack Monitoring] add monitoring.ui.elasticsearch.serviceAccountToken to docs (#128488) * monitoring.ui.elasticsearch.serviceAccountToken to docs * update description * update * update to make clearer * Update docs/settings/monitoring-settings.asciidoc Co-authored-by: Joe Portner <5295965+jportner@users.noreply.github.com> * Update docs/settings/monitoring-settings.asciidoc Co-authored-by: Joe Portner <5295965+jportner@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Joe Portner <5295965+jportner@users.noreply.github.com> --- docs/settings/monitoring-settings.asciidoc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/settings/monitoring-settings.asciidoc b/docs/settings/monitoring-settings.asciidoc index 8b469b1a90194..e78aeb6bac00b 100644 --- a/docs/settings/monitoring-settings.asciidoc +++ b/docs/settings/monitoring-settings.asciidoc @@ -50,10 +50,10 @@ cluster. `monitoring.ui.elasticsearch.username`:: Specifies the username used by {kib} monitoring to establish a persistent connection -in {kib} to the {es} monitoring cluster and to verify licensing status on the {es} -monitoring cluster. +in {kib} to the {es} monitoring cluster and to verify licensing status on the {es} +monitoring cluster when using `monitoring.ui.elasticsearch.hosts`. + -Every other request performed by *{stack-monitor-app}* to the monitoring {es} +All other requests performed by *{stack-monitor-app}* to the monitoring {es} cluster uses the authenticated user's credentials, which must be the same on both the {es} monitoring cluster and the {es} production cluster. + @@ -62,14 +62,17 @@ If not set, {kib} uses the value of the <> setting. +`monitoring.ui.elasticsearch.serviceAccountToken`:: +Specifies a {ref}/security-api-create-service-token.html[service account token] for the {es} cluster where your monitoring data is stored when using `monitoring.ui.elasticsearch.hosts`. This setting is an alternative to using `monitoring.ui.elasticsearch.username` and `monitoring.ui.elasticsearch.password`. + `monitoring.ui.elasticsearch.pingTimeout`:: Specifies the time in milliseconds to wait for {es} to respond to internal health checks. By default, it matches the <> setting, From 95ba4f8d93829bca37970b87e18bec9a7b43a062 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Tue, 29 Mar 2022 15:54:03 +0200 Subject: [PATCH 21/37] [Discover] Cleanup usage of deprecated APIs (#128713) --- .../actions/explore_data/explore_data_chart_action.test.ts | 3 +-- .../public/actions/explore_data/explore_data_chart_action.ts | 4 ++-- .../actions/explore_data/explore_data_context_menu_action.ts | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_chart_action.test.ts b/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_chart_action.test.ts index b8f44bdbc9a25..f4c6e89280274 100644 --- a/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_chart_action.test.ts +++ b/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_chart_action.test.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import type { Filter, RangeFilter } from '@kbn/es-query'; import { ExploreDataChartAction } from './explore_data_chart_action'; import { Params, PluginDeps } from './abstract_explore_data_action'; import { coreMock } from '../../../../../../src/core/public/mocks'; @@ -15,7 +15,6 @@ import { VISUALIZE_EMBEDDABLE_TYPE, } from '../../../../../../src/plugins/visualizations/public'; import { ViewMode } from '../../../../../../src/plugins/embeddable/public'; -import { Filter, RangeFilter } from '../../../../../../src/plugins/data/public'; import { DiscoverAppLocator } from '../../../../../../src/plugins/discover/public'; import { sharePluginMock } from '../../../../../../src/plugins/share/public/mocks'; diff --git a/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_chart_action.ts b/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_chart_action.ts index 4ab9146a06c55..df85390ba0493 100644 --- a/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_chart_action.ts +++ b/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_chart_action.ts @@ -12,7 +12,7 @@ import { } from '../../../../../../src/plugins/discover/public'; import { ApplyGlobalFilterActionContext, - esFilters, + extractTimeRange, } from '../../../../../../src/plugins/data/public'; import { IEmbeddable } from '../../../../../../src/plugins/embeddable/public'; import { KibanaLocation } from '../../../../../../src/plugins/share/public'; @@ -55,7 +55,7 @@ export class ExploreDataChartAction } const { embeddable } = context; - const { restOfFilters: filters, timeRange } = esFilters.extractTimeRange( + const { restOfFilters: filters, timeRange } = extractTimeRange( context.filters, context.timeFieldName ); diff --git a/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_context_menu_action.ts b/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_context_menu_action.ts index 3580a3f191e6c..28f41fdb18711 100644 --- a/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_context_menu_action.ts +++ b/x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_context_menu_action.ts @@ -4,14 +4,14 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import type { Filter } from '@kbn/es-query'; import { Action } from '../../../../../../src/plugins/ui_actions/public'; import { EmbeddableContext, EmbeddableInput, IEmbeddable, } from '../../../../../../src/plugins/embeddable/public'; -import { Query, Filter, TimeRange } from '../../../../../../src/plugins/data/public'; +import { Query, TimeRange } from '../../../../../../src/plugins/data/public'; import { DiscoverAppLocatorParams } from '../../../../../../src/plugins/discover/public'; import { KibanaLocation } from '../../../../../../src/plugins/share/public'; import * as shared from './shared'; From 3fc2a3940f1afc96ebad2b8bb2ce7545548ec5ae Mon Sep 17 00:00:00 2001 From: Milton Hultgren Date: Tue, 29 Mar 2022 16:29:50 +0200 Subject: [PATCH 22/37] [Infra Monitoring UI] Add docs for Stack Monitoring template versioning (#128762) * [Infra Monitoring UI] Add doc notes for Stack Monitoring template versioning * Add link to reference PR --- x-pack/plugins/monitoring/dev_docs/reference/indices.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x-pack/plugins/monitoring/dev_docs/reference/indices.md b/x-pack/plugins/monitoring/dev_docs/reference/indices.md index 5f4fd5d498d9a..c1c95d26842a7 100644 --- a/x-pack/plugins/monitoring/dev_docs/reference/indices.md +++ b/x-pack/plugins/monitoring/dev_docs/reference/indices.md @@ -29,6 +29,10 @@ The index templates for `.monitoring-*` are shipped with and managed by Elastics To verify changes to these templates, either make them in place on a running cluster or run elasticsearch from source. +When updating the templates, it is important to increment the version number [here](https://github.com/elastic/elasticsearch/blob/main/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringTemplateRegistry.java#L81). Elasticsearch uses this version number to decide if it should re-install the templates. +PRs should add the labels ":Data Management/Monitoring" and "Team:Data Management" to involve the right Elasticsearch members. +[Reference PR](https://github.com/elastic/elasticsearch/pull/85447) + The `metrics-*` and `metricbeat-*` mappings are managed by metricbeat and elastic agent, **code locations TBD**. ## Aliasing From 06fd7e76acef7a4d590cc40bf7ba61bba7cfe213 Mon Sep 17 00:00:00 2001 From: Or Ouziel Date: Tue, 29 Mar 2022 17:32:54 +0300 Subject: [PATCH 23/37] [Cloud Posture] add rules table flyout (#127887) --- .../public/pages/rules/rules_bottom_bar.tsx | 7 +- .../pages/rules/rules_container.test.tsx | 40 ++++- .../public/pages/rules/rules_container.tsx | 3 +- .../public/pages/rules/rules_flyout.tsx | 146 +++++++++++++++++- .../public/pages/rules/rules_table.tsx | 20 +-- .../public/pages/rules/test_subjects.ts | 6 + .../public/pages/rules/translations.ts | 24 +++ 7 files changed, 218 insertions(+), 28 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_bottom_bar.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_bottom_bar.tsx index ebf4913f895c3..1cd860f74fbbe 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_bottom_bar.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_bottom_bar.tsx @@ -16,7 +16,12 @@ interface RulesBottomBarProps { } export const RulesBottomBar = ({ onSave, onCancel, isLoading }: RulesBottomBarProps) => ( - + diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.test.tsx index bcbc4d5c4bd44..7782be9c4917e 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { RulesContainer } from './rules_container'; -import { render, screen, fireEvent } from '@testing-library/react'; +import { render, screen, fireEvent, within } from '@testing-library/react'; import { QueryClient } from 'react-query'; import { useFindCspRules, useBulkUpdateCspRules, type RuleSavedObject } from './use_csp_rules'; import * as TEST_SUBJECTS from './test_subjects'; @@ -38,7 +38,7 @@ const getRuleMock = ({ id = chance.guid(), enabled }: { id?: string; enabled: bo updatedAt: chance.date().toISOString(), attributes: { id, - name: chance.word(), + name: chance.sentence(), enabled, }, } as RuleSavedObject); @@ -336,4 +336,40 @@ describe('', () => { })) ); }); + + it('updates the rules from within the flyout', () => { + const Wrapper = getWrapper(); + const enabled = true; + const rules = Array.from({ length: 20 }, () => getRuleMock({ enabled })); + + (useFindCspRules as jest.Mock).mockReturnValue({ + status: 'success', + data: { + total: rules.length, + savedObjects: rules, + }, + }); + + render( + + + + ); + + const rule = rules[0]; + const rowId = TEST_SUBJECTS.getCspRulesTableRowItemTestId(rule.id); + const switchId = TEST_SUBJECTS.getCspRulesTableItemSwitchTestId(rule.id); + + fireEvent.click(screen.getByTestId(rowId)); // open flyout + + const flyout = screen.getByTestId(TEST_SUBJECTS.CSP_RULES_FLYOUT_CONTAINER); + + fireEvent.click(within(flyout).getByTestId(switchId)); // change to !enabled + fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_SAVE_BUTTON)); // save + + const { mutate } = useBulkUpdateCspRules(); + + expect(mutate).toHaveBeenCalledTimes(1); + expect(mutate).toHaveBeenCalledWith([{ ...rule.attributes, enabled: !enabled }]); + }); }); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.tsx index 9780f9ecd3778..96659337e0c1a 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.tsx @@ -214,8 +214,9 @@ export const RulesContainer = () => { )} {selectedRuleId && ( setSelectedRuleId(null)} + toggleRule={toggleRule} /> )} diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_flyout.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_flyout.tsx index 8e8e0e37eed0d..54657660c7aa4 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_flyout.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_flyout.tsx @@ -4,23 +4,155 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React from 'react'; -import { EuiSpacer, EuiFlyout, EuiFlyoutHeader, EuiFlyoutBody } from '@elastic/eui'; +import React, { useState } from 'react'; +import { + EuiSpacer, + EuiFlyout, + type EuiDescriptionListProps, + EuiToolTip, + EuiFlyoutHeader, + EuiFlyoutBody, + EuiTab, + EuiTabs, + EuiTitle, + EuiDescriptionList, + EuiFlexItem, + EuiFlexGroup, + EuiSwitch, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import type { RuleSavedObject } from './use_csp_rules'; +import * as TEXT from './translations'; +import * as TEST_SUBJECTS from './test_subjects'; -interface FindingFlyoutProps { +interface RuleFlyoutProps { onClose(): void; + toggleRule(rule: RuleSavedObject): void; rule: RuleSavedObject; } -export const RuleFlyout = ({ onClose, rule }: FindingFlyoutProps) => { +const tabs = [ + { label: TEXT.OVERVIEW, id: 'overview', disabled: false }, + { label: TEXT.REMEDIATION, id: 'remediation', disabled: false }, + { label: TEXT.REGO_CODE, id: 'rego', disabled: true }, +] as const; + +type RuleTab = typeof tabs[number]['id']; + +const getOverviewCard = (rule: RuleSavedObject): EuiDescriptionListProps['listItems'] => [ + { + title: i18n.translate('xpack.csp.rules.ruleFlyout.frameworkSourcesLabel', { + defaultMessage: 'Framework Sources', + }), + description: '-', // TODO: add value + }, + { + title: i18n.translate('xpack.csp.rules.ruleFlyout.sectionsLabel', { + defaultMessage: 'Sections', + }), + description: '-', // TODO: add value + }, + { + title: i18n.translate('xpack.csp.rules.ruleFlyout.profileApplicabilityLabel', { + defaultMessage: 'Profile Applicability', + }), + description: rule.attributes.description || '', + }, + { + title: i18n.translate('xpack.csp.rules.ruleFlyout.auditLabel', { + defaultMessage: 'Audit', + }), + description: '-', // TODO: add value + }, + { + title: i18n.translate('xpack.csp.rules.ruleFlyout.referencesLabel', { + defaultMessage: 'References', + }), + description: '-', // TODO: add value + }, +]; + +const getRemediationCard = (rule: RuleSavedObject): EuiDescriptionListProps['listItems'] => [ + { + title: i18n.translate('xpack.csp.rules.ruleFlyout.remediationLabel', { + defaultMessage: 'Remediation', + }), + description: rule.attributes.remediation, + }, + { + title: i18n.translate('xpack.csp.rules.ruleFlyout.impactLabel', { + defaultMessage: 'Impact', + }), + description: rule.attributes.impact, + }, + { + title: i18n.translate('xpack.csp.rules.ruleFlyout.defaultValueLabel', { + defaultMessage: 'Default Value', + }), + description: rule.attributes.default_value, + }, + { + title: i18n.translate('xpack.csp.rules.ruleFlyout.rationaleLabel', { + defaultMessage: 'Rationale', + }), + description: rule.attributes.rationale, + }, +]; + +export const RuleFlyout = ({ onClose, rule, toggleRule }: RuleFlyoutProps) => { + const [tab, setTab] = useState('overview'); + return ( - + -

{rule.attributes.name}

+ +

{rule.attributes.name}

+
+ + {tabs.map((item) => ( + setTab(item.id)} + disabled={item.disabled} + > + {item.label} + + ))} +
- + + {tab === 'overview' && toggleRule(rule)} />} + {tab === 'remediation' && ( + + )} +
); }; + +const RuleOverviewTab = ({ rule, toggleRule }: { rule: RuleSavedObject; toggleRule(): void }) => ( + + + + + + + + + + + + +); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table.tsx index e8fd704e124f4..c577daebb58a6 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table.tsx @@ -16,7 +16,6 @@ import { useEuiTheme, } from '@elastic/eui'; import moment from 'moment'; -import { FormattedMessage } from '@kbn/i18n-react'; import type { RulesState } from './rules_container'; import * as TEST_SUBJECTS from './test_subjects'; import * as TEXT from './translations'; @@ -60,7 +59,6 @@ export const RulesTable = ({ pageSize, totalItemCount: total, pageSizeOptions: [1, 5, 10, 25], - showPerPageOptions: true, }; const selection: EuiBasicTableProps['selection'] = { @@ -74,6 +72,7 @@ export const RulesTable = ({ }; const rowProps = (row: RuleSavedObject) => ({ + ['data-test-subj']: TEST_SUBJECTS.getCspRulesTableRowItemTestId(row.id), style: { background: row.id === selectedRuleId ? euiTheme.colors.highlight : undefined }, onClick: (e: MouseEvent) => { const tag = (e.target as HTMLDivElement).tagName; @@ -122,6 +121,7 @@ const getColumns = ({ e.stopPropagation(); setSelectedRuleId(rule.id); }} + data-test-subj={TEST_SUBJECTS.CSP_RULES_TABLE_ROW_ITEM_NAME} > {name} @@ -142,21 +142,7 @@ const getColumns = ({ field: 'attributes.enabled', name: TEXT.ENABLED, render: (enabled, rule) => ( - - ) : ( - - ) - } - > + `${CSP_RULES_TABLE_ITEM_SWITCH}_${id}`; + +export const getCspRulesTableRowItemTestId = (id: string) => + `${CSP_RULES_TABLE_ROW_ITEM_NAME}_${id}`; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/translations.ts b/x-pack/plugins/cloud_security_posture/public/pages/rules/translations.ts index 8523e0afc06c5..a10e4b2efc1fb 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/translations.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/translations.ts @@ -56,6 +56,18 @@ export const ENABLE = i18n.translate('xpack.csp.rules.enableLabel', { defaultMessage: 'Enable', }); +export const DEACTIVATE = i18n.translate('xpack.csp.rules.deactivateLabel', { + defaultMessage: 'Deactivate', +}); + +export const ACTIVATE = i18n.translate('xpack.csp.rules.activateLabel', { + defaultMessage: 'Activate', +}); + +export const ACTIVATED = i18n.translate('xpack.csp.rules.activatedLabel', { + defaultMessage: 'Activated', +}); + export const MISSING_RULES = i18n.translate('xpack.csp.rules.missingRulesMessage', { defaultMessage: 'Rules are missing', }); @@ -63,3 +75,15 @@ export const MISSING_RULES = i18n.translate('xpack.csp.rules.missingRulesMessage export const UPDATE_FAILED = i18n.translate('xpack.csp.rules.updateFailedMessage', { defaultMessage: 'Update failed', }); + +export const OVERVIEW = i18n.translate('xpack.csp.rules.ruleFlyout.tabs.overviewTabLabel', { + defaultMessage: 'Overview', +}); + +export const REGO_CODE = i18n.translate('xpack.csp.rules.ruleFlyout.tabs.regoCodeTabLabel', { + defaultMessage: 'Rego Code', +}); + +export const REMEDIATION = i18n.translate('xpack.csp.rules.ruleFlyout.tabs.remediationTabLabel', { + defaultMessage: 'Remediation', +}); From 5cf462bad7e0bbc020daadedd53d611534bfc427 Mon Sep 17 00:00:00 2001 From: Madison Caldwell Date: Tue, 29 Mar 2022 10:35:31 -0400 Subject: [PATCH 24/37] Add fixtures generated by kbn-gen-fixtures (#128419) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../tests/alerts/alerts_compatibility.ts | 416 +- .../security_solution/alerts/7.16.0/data.json | 3590 ++++++++++ .../alerts/7.16.0/mappings.json | 5819 +++++++++++++++++ 3 files changed, 9608 insertions(+), 217 deletions(-) create mode 100644 x-pack/test/functional/es_archives/security_solution/alerts/7.16.0/data.json create mode 100644 x-pack/test/functional/es_archives/security_solution/alerts/7.16.0/mappings.json diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/alerts/alerts_compatibility.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/alerts/alerts_compatibility.ts index 889396c2b6125..953dfb42b7b46 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/alerts/alerts_compatibility.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/alerts/alerts_compatibility.ts @@ -46,21 +46,6 @@ export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); describe('Alerts Compatibility', function () { - beforeEach(async () => { - await esArchiver.load( - 'x-pack/test/functional/es_archives/security_solution/legacy_cti_signals' - ); - await createSignalsIndex(supertest, log); - }); - - afterEach(async () => { - await esArchiver.unload( - 'x-pack/test/functional/es_archives/security_solution/legacy_cti_signals' - ); - await deleteSignalsIndex(supertest, log); - await deleteAllAlerts(supertest, log); - }); - describe('CTI', () => { const expectedDomain = 'elastic.local'; const expectedProvider = 'provider1'; @@ -72,6 +57,21 @@ export default ({ getService }: FtrProviderContext) => { type: 'indicator_match_rule', }; + beforeEach(async () => { + await esArchiver.load( + 'x-pack/test/functional/es_archives/security_solution/legacy_cti_signals' + ); + await createSignalsIndex(supertest, log); + }); + + afterEach(async () => { + await esArchiver.unload( + 'x-pack/test/functional/es_archives/security_solution/legacy_cti_signals' + ); + await deleteSignalsIndex(supertest, log); + await deleteAllAlerts(supertest, log); + }); + it('allows querying of legacy enriched signals by threat.indicator', async () => { const { body: { @@ -208,6 +208,19 @@ export default ({ getService }: FtrProviderContext) => { }); describe('Query', () => { + beforeEach(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/security_solution/alerts/7.16.0'); + await createSignalsIndex(supertest, log); + }); + + afterEach(async () => { + await esArchiver.unload( + 'x-pack/test/functional/es_archives/security_solution/alerts/7.16.0' + ); + await deleteSignalsIndex(supertest, log); + await deleteAllAlerts(supertest, log); + }); + it('should generate a signal-on-legacy-signal with legacy index pattern', async () => { const rule: QueryCreateSchema = getRuleForSignalTesting([`.siem-signals-*`]); const { id } = await createRule(supertest, log, rule); @@ -236,121 +249,87 @@ export default ({ getService }: FtrProviderContext) => { 'kibana.space_ids': ['default'], 'kibana.alert.rule.tags': [], agent: { - ephemeral_id: '07c24b1e-3663-4372-b982-f2d831e033eb', - hostname: 'elastic.local', - id: 'ce7741d9-3f0a-466d-8ae6-d7d8f883fcec', - name: 'elastic.local', - type: 'auditbeat', - version: '7.14.0', + name: 'security-linux-1.example.dev', + id: 'd8f66724-3cf2-437c-b124-6ac9fb0e2311', + type: 'filebeat', + version: '7.16.0', }, - ecs: { version: '1.10.0' }, - host: { - architecture: 'x86_64', - hostname: 'elastic.local', - id: '1633D595-A115-5BF5-870B-A471B49446C3', - ip: ['192.168.1.1'], - mac: ['aa:bb:cc:dd:ee:ff'], - name: 'elastic.local', - os: { - build: '20G80', - family: 'darwin', - kernel: '20.6.0', - name: 'Mac OS X', - platform: 'darwin', - type: 'macos', - version: '10.16', + log: { + file: { + path: '/opt/Elastic/Agent/data/elastic-agent-a13c93/logs/default/filebeat-20220301-3.ndjson', }, + offset: 148938, }, - message: 'Process mdworker_shared (PID: 32306) by user elastic STARTED', - process: { - args: [ - '/System/Library/Frameworks/CoreServices.framework/Frameworks/Metadata.framework/Versions/A/Support/mdworker_shared', - '-s', - 'mdworker', - '-c', - 'MDSImporterWorker', - '-m', - 'com.apple.mdworker.shared', - ], - entity_id: 'wfc7zUuEinqxUbZ6', - executable: - '/System/Library/Frameworks/CoreServices.framework/Frameworks/Metadata.framework/Versions/A/Support/mdworker_shared', - hash: { sha1: '5f3233fd75c14b315731684d59b632df36a731a6' }, - name: 'mdworker_shared', - pid: 32306, - ppid: 1, - start: '2021-08-04T04:14:48.830Z', - working_directory: '/', + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'security-linux-1', + id: '8995531128842994872', + }, + provider: 'gcp', + service: { + name: 'GCE', + }, + machine: { + type: 'g1-small', + }, + project: { + id: 'elastic-siem', + }, + account: { + id: 'elastic-siem', + }, }, - service: { type: 'system' }, - threat: { - indicator: [ - { - domain: 'elastic.local', - event: { - category: 'threat', - created: '2021-08-04T03:53:30.761Z', - dataset: 'ti_abusech.malware', - ingested: '2021-08-04T03:53:37.514040Z', - kind: 'enrichment', - module: 'threatintel', - reference: 'https://urlhaus.abuse.ch/url/12345/', - type: 'indicator', - }, - first_seen: '2021-08-03T20:35:17.000Z', - matched: { - atomic: 'elastic.local', - field: 'host.name', - id: '_tdUD3sBcVT20cvWAkpd', - index: 'filebeat-7.14.0-2021.08.04-000001', - type: 'indicator_match_rule', - }, - provider: 'provider1', - type: 'url', - url: { - domain: 'elastic.local', - extension: 'php', - full: 'http://elastic.local/thing', - original: 'http://elastic.local/thing', - path: '/thing', - scheme: 'http', - }, - }, - ], + ecs: { + version: '7.16.0', + }, + host: { + hostname: 'security-linux-1', + os: { + kernel: '4.19.0-18-cloud-amd64', + codename: 'buster', + name: 'Debian GNU/Linux', + type: 'linux', + family: 'debian', + version: '10 (buster)', + platform: 'debian', + }, + containerized: false, + ip: '11.200.0.194', + name: 'security-linux-1', + architecture: 'x86_64', }, - user: { - effective: { group: { id: '20' }, id: '501' }, - group: { id: '20', name: 'staff' }, - id: '501', - name: 'elastic', - saved: { group: { id: '20' }, id: '501' }, + 'service.name': 'filebeat', + message: 'Status message.', + data_stream: { + namespace: 'default', + type: 'logs', + dataset: 'elastic_agent.filebeat', }, - 'event.action': 'process_started', - 'event.category': ['process'], - 'event.dataset': 'process', + 'event.agent_id_status': 'verified', + 'event.ingested': '2022-03-23T16:50:28.994Z', + 'event.dataset': 'elastic_agent.filebeat', 'event.kind': 'signal', - 'event.module': 'system', - 'event.type': ['start'], 'kibana.alert.ancestors': [ { - depth: 0, - id: 'yNdfD3sBcVT20cvWFEs2', - index: 'auditbeat-7.14.0-2021.08.04-000001', + id: 'Nmyvt38BIyEvspK02HTJ', type: 'event', + index: 'events-index-000001', + depth: 0, }, { - id: '0527411874b23bcea85daf5bf7dcacd144536ba6d92d3230a4a0acfb7de7f512', + id: '5cddda6852c5f8b6c32d4bfa5e876aa51884e0c7a2d4faaababf91ec9cb68de7', type: 'signal', - index: '.siem-signals-default-000001', + index: '.siem-signals-default-000001-7.16.0', depth: 1, - rule: '832f86f0-f4da-11eb-989d-b758d09dbc85', + rule: '5b7cd9a0-aac9-11ec-bb53-fd375b7a173a', }, ], 'kibana.alert.status': 'active', 'kibana.alert.workflow_status': 'open', 'kibana.alert.depth': 2, 'kibana.alert.reason': - 'process event with process mdworker_shared, by elastic on elastic.local created high alert Signal Testing Query.', + 'event on security-linux-1 created high alert Signal Testing Query.', 'kibana.alert.severity': 'high', 'kibana.alert.risk_score': 1, 'kibana.alert.rule.parameters': { @@ -397,13 +376,11 @@ export default ({ getService }: FtrProviderContext) => { 'kibana.alert.rule.version': 1, 'kibana.alert.rule.exceptions_list': [], 'kibana.alert.rule.immutable': false, - 'kibana.alert.original_time': '2021-08-04T04:14:58.973Z', - 'kibana.alert.original_event.action': 'process_started', - 'kibana.alert.original_event.category': ['process'], - 'kibana.alert.original_event.dataset': 'process', + 'kibana.alert.original_time': '2022-03-23T16:50:40.440Z', + 'kibana.alert.original_event.agent_id_status': 'verified', + 'kibana.alert.original_event.ingested': '2022-03-23T16:50:28.994Z', + 'kibana.alert.original_event.dataset': 'elastic_agent.filebeat', 'kibana.alert.original_event.kind': 'signal', - 'kibana.alert.original_event.module': 'system', - 'kibana.alert.original_event.type': ['start'], }); }); @@ -437,121 +414,87 @@ export default ({ getService }: FtrProviderContext) => { 'kibana.space_ids': ['default'], 'kibana.alert.rule.tags': [], agent: { - ephemeral_id: '07c24b1e-3663-4372-b982-f2d831e033eb', - hostname: 'elastic.local', - id: 'ce7741d9-3f0a-466d-8ae6-d7d8f883fcec', - name: 'elastic.local', - type: 'auditbeat', - version: '7.14.0', + name: 'security-linux-1.example.dev', + id: 'd8f66724-3cf2-437c-b124-6ac9fb0e2311', + type: 'filebeat', + version: '7.16.0', }, - ecs: { version: '1.10.0' }, - host: { - architecture: 'x86_64', - hostname: 'elastic.local', - id: '1633D595-A115-5BF5-870B-A471B49446C3', - ip: ['192.168.1.1'], - mac: ['aa:bb:cc:dd:ee:ff'], - name: 'elastic.local', - os: { - build: '20G80', - family: 'darwin', - kernel: '20.6.0', - name: 'Mac OS X', - platform: 'darwin', - type: 'macos', - version: '10.16', + log: { + file: { + path: '/opt/Elastic/Agent/data/elastic-agent-a13c93/logs/default/filebeat-20220301-3.ndjson', }, + offset: 148938, }, - message: 'Process mdworker_shared (PID: 32306) by user elastic STARTED', - process: { - args: [ - '/System/Library/Frameworks/CoreServices.framework/Frameworks/Metadata.framework/Versions/A/Support/mdworker_shared', - '-s', - 'mdworker', - '-c', - 'MDSImporterWorker', - '-m', - 'com.apple.mdworker.shared', - ], - entity_id: 'wfc7zUuEinqxUbZ6', - executable: - '/System/Library/Frameworks/CoreServices.framework/Frameworks/Metadata.framework/Versions/A/Support/mdworker_shared', - hash: { sha1: '5f3233fd75c14b315731684d59b632df36a731a6' }, - name: 'mdworker_shared', - pid: 32306, - ppid: 1, - start: '2021-08-04T04:14:48.830Z', - working_directory: '/', + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'security-linux-1', + id: '8995531128842994872', + }, + provider: 'gcp', + service: { + name: 'GCE', + }, + machine: { + type: 'g1-small', + }, + project: { + id: 'elastic-siem', + }, + account: { + id: 'elastic-siem', + }, }, - service: { type: 'system' }, - threat: { - indicator: [ - { - domain: 'elastic.local', - event: { - category: 'threat', - created: '2021-08-04T03:53:30.761Z', - dataset: 'ti_abusech.malware', - ingested: '2021-08-04T03:53:37.514040Z', - kind: 'enrichment', - module: 'threatintel', - reference: 'https://urlhaus.abuse.ch/url/12345/', - type: 'indicator', - }, - first_seen: '2021-08-03T20:35:17.000Z', - matched: { - atomic: 'elastic.local', - field: 'host.name', - id: '_tdUD3sBcVT20cvWAkpd', - index: 'filebeat-7.14.0-2021.08.04-000001', - type: 'indicator_match_rule', - }, - provider: 'provider1', - type: 'url', - url: { - domain: 'elastic.local', - extension: 'php', - full: 'http://elastic.local/thing', - original: 'http://elastic.local/thing', - path: '/thing', - scheme: 'http', - }, - }, - ], + ecs: { + version: '7.16.0', + }, + host: { + hostname: 'security-linux-1', + os: { + kernel: '4.19.0-18-cloud-amd64', + codename: 'buster', + name: 'Debian GNU/Linux', + type: 'linux', + family: 'debian', + version: '10 (buster)', + platform: 'debian', + }, + containerized: false, + ip: '11.200.0.194', + name: 'security-linux-1', + architecture: 'x86_64', }, - user: { - effective: { group: { id: '20' }, id: '501' }, - group: { id: '20', name: 'staff' }, - id: '501', - name: 'elastic', - saved: { group: { id: '20' }, id: '501' }, + 'service.name': 'filebeat', + message: 'Status message.', + data_stream: { + namespace: 'default', + type: 'logs', + dataset: 'elastic_agent.filebeat', }, - 'event.action': 'process_started', - 'event.category': ['process'], - 'event.dataset': 'process', + 'event.agent_id_status': 'verified', + 'event.ingested': '2022-03-23T16:50:28.994Z', + 'event.dataset': 'elastic_agent.filebeat', 'event.kind': 'signal', - 'event.module': 'system', - 'event.type': ['start'], 'kibana.alert.ancestors': [ { - depth: 0, - id: 'yNdfD3sBcVT20cvWFEs2', - index: 'auditbeat-7.14.0-2021.08.04-000001', + id: 'Nmyvt38BIyEvspK02HTJ', type: 'event', + index: 'events-index-000001', + depth: 0, }, { - id: '0527411874b23bcea85daf5bf7dcacd144536ba6d92d3230a4a0acfb7de7f512', + id: '5cddda6852c5f8b6c32d4bfa5e876aa51884e0c7a2d4faaababf91ec9cb68de7', type: 'signal', - index: '.siem-signals-default-000001', + index: '.siem-signals-default-000001-7.16.0', depth: 1, - rule: '832f86f0-f4da-11eb-989d-b758d09dbc85', + rule: '5b7cd9a0-aac9-11ec-bb53-fd375b7a173a', }, ], 'kibana.alert.status': 'active', 'kibana.alert.workflow_status': 'open', 'kibana.alert.depth': 2, 'kibana.alert.reason': - 'process event with process mdworker_shared, by elastic on elastic.local created high alert Signal Testing Query.', + 'event on security-linux-1 created high alert Signal Testing Query.', 'kibana.alert.severity': 'high', 'kibana.alert.risk_score': 1, 'kibana.alert.rule.parameters': { @@ -598,18 +541,29 @@ export default ({ getService }: FtrProviderContext) => { 'kibana.alert.rule.version': 1, 'kibana.alert.rule.exceptions_list': [], 'kibana.alert.rule.immutable': false, - 'kibana.alert.original_time': '2021-08-04T04:14:58.973Z', - 'kibana.alert.original_event.action': 'process_started', - 'kibana.alert.original_event.category': ['process'], - 'kibana.alert.original_event.dataset': 'process', + 'kibana.alert.original_time': '2022-03-23T16:50:40.440Z', + 'kibana.alert.original_event.agent_id_status': 'verified', + 'kibana.alert.original_event.ingested': '2022-03-23T16:50:28.994Z', + 'kibana.alert.original_event.dataset': 'elastic_agent.filebeat', 'kibana.alert.original_event.kind': 'signal', - 'kibana.alert.original_event.module': 'system', - 'kibana.alert.original_event.type': ['start'], }); }); }); describe('Saved Query', () => { + beforeEach(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/security_solution/alerts/7.16.0'); + await createSignalsIndex(supertest, log); + }); + + afterEach(async () => { + await esArchiver.unload( + 'x-pack/test/functional/es_archives/security_solution/alerts/7.16.0' + ); + await deleteSignalsIndex(supertest, log); + await deleteAllAlerts(supertest, log); + }); + it('should generate a signal-on-legacy-signal with legacy index pattern', async () => { const rule: SavedQueryCreateSchema = getSavedQueryRuleForSignalTesting([`.siem-signals-*`]); const { id } = await createRule(supertest, log, rule); @@ -636,6 +590,19 @@ export default ({ getService }: FtrProviderContext) => { }); describe('EQL', () => { + beforeEach(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/security_solution/alerts/7.16.0'); + await createSignalsIndex(supertest, log); + }); + + afterEach(async () => { + await esArchiver.unload( + 'x-pack/test/functional/es_archives/security_solution/alerts/7.16.0' + ); + await deleteSignalsIndex(supertest, log); + await deleteAllAlerts(supertest, log); + }); + it('should generate a signal-on-legacy-signal with legacy index pattern', async () => { const rule: EqlCreateSchema = getEqlRuleForSignalTesting(['.siem-signals-*']); const { id } = await createRule(supertest, log, rule); @@ -662,6 +629,19 @@ export default ({ getService }: FtrProviderContext) => { }); describe('Threshold', () => { + beforeEach(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/security_solution/alerts/7.16.0'); + await createSignalsIndex(supertest, log); + }); + + afterEach(async () => { + await esArchiver.unload( + 'x-pack/test/functional/es_archives/security_solution/alerts/7.16.0' + ); + await deleteSignalsIndex(supertest, log); + await deleteAllAlerts(supertest, log); + }); + it('should generate a signal-on-legacy-signal with legacy index pattern', async () => { const baseRule: ThresholdCreateSchema = getThresholdRuleForSignalTesting([ '.siem-signals-*', @@ -670,6 +650,7 @@ export default ({ getService }: FtrProviderContext) => { ...baseRule, threshold: { ...baseRule.threshold, + field: 'host.name', value: 1, }, }; @@ -690,6 +671,7 @@ export default ({ getService }: FtrProviderContext) => { ...baseRule, threshold: { ...baseRule.threshold, + field: 'host.name', value: 1, }, }; diff --git a/x-pack/test/functional/es_archives/security_solution/alerts/7.16.0/data.json b/x-pack/test/functional/es_archives/security_solution/alerts/7.16.0/data.json new file mode 100644 index 0000000000000..9f15ea353570e --- /dev/null +++ b/x-pack/test/functional/es_archives/security_solution/alerts/7.16.0/data.json @@ -0,0 +1,3590 @@ +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "0bb0c0d5488d757907f6be6e4c27ff698666948e2cf01d53e8fa43958b36c6a8", + "source": { + "agent": { + "name": "security-linux-1.example.dev", + "id": "d8f66724-3cf2-437c-b124-6ac9fb0e2311", + "type": "filebeat", + "version": "7.16.0" + }, + "log": { + "file": { + "path": "/opt/Elastic/Agent/data/elastic-agent-a13c93/logs/default/filebeat-20220301-3.ndjson" + }, + "offset": 148938 + }, + "cloud": { + "availability_zone": "us-central1-c", + "instance": { + "name": "security-linux-1", + "id": "8995531128842994872" + }, + "provider": "gcp", + "service": { + "name": "GCE" + }, + "machine": { + "type": "g1-small" + }, + "project": { + "id": "elastic-siem" + }, + "account": { + "id": "elastic-siem" + } + }, + "ecs": { + "version": "7.16.0" + }, + "host": { + "hostname": "security-linux-1", + "os": { + "kernel": "4.19.0-18-cloud-amd64", + "codename": "buster", + "name": "Debian GNU/Linux", + "type": "linux", + "family": "debian", + "version": "10 (buster)", + "platform": "debian" + }, + "containerized": false, + "ip": "11.200.0.194", + "name": "security-linux-1", + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:32.045Z", + "dataset": "elastic_agent.filebeat", + "kind": "signal" + }, + "service.name": "filebeat", + "message": "Status message.", + "@timestamp": "2022-03-23T16:50:51.493Z", + "data_stream": { + "namespace": "default", + "type": "logs", + "dataset": "elastic_agent.filebeat" + }, + "threat": { + "enrichments": [ + { + "indicator": {}, + "matched": { + "atomic": "security-linux-1", + "field": "host.name", + "id": "M2yvt38BIyEvspK01XQt", + "index": "threat-index-000001", + "type": "indicator_match_rule" + } + } + ] + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "P2yvt38BIyEvspK05HSe", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "P2yvt38BIyEvspK05HSe", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "62f9a8c0-aac9-11ec-aa31-c9ea2cb79db7", + "actions": [], + "interval": "1m", + "name": "threat-match-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:47.264Z", + "updated_at": "2022-03-23T16:50:48.396Z", + "description": "a simple threat match rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "bef9b0da-8c2f-4b82-930f-37ffa1b57fc1", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "threat_match", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [], + "threat_query": "*", + "threat_mapping": [ + { + "entries": [ + { + "field": "host.name", + "type": "mapping", + "value": "host.name" + } + ] + } + ], + "threat_language": "kuery", + "threat_index": [ + "threat-index-*" + ], + "threat_indicator_path": "threat.indicator" + }, + "reason": "event on security-linux-1 created low alert threat-match-rule.", + "depth": 1, + "parent": { + "id": "P2yvt38BIyEvspK05HSe", + "type": "event", + "index": "events-index-000001", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:32.045Z", + "original_event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:32.045Z", + "dataset": "elastic_agent.filebeat" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "0dd11069ba6c63ec60ac902d6fb0a8a52c4f5ab20f03babe7b861c6d34431bad", + "source": { + "agent": { + "name": "security-linux-3.example.dev", + "id": "06851da1-73e7-41e3-97d6-ff1d62c98dc5", + "type": "filebeat", + "version": "7.16.0" + }, + "log": { + "file": { + "path": "/opt/Elastic/Agent/data/elastic-agent-a13c93/logs/default/filebeat-20220301-3.ndjson" + }, + "offset": 148938 + }, + "cloud": { + "availability_zone": "us-central1-c", + "instance": { + "name": "security-linux-1", + "id": "8995531128842994872" + }, + "provider": "gcp", + "service": { + "name": "GCE" + }, + "machine": { + "type": "g1-small" + }, + "project": { + "id": "elastic-siem" + }, + "account": { + "id": "elastic-siem" + } + }, + "ecs": { + "version": "7.16.0" + }, + "host": { + "hostname": "security-linux-3", + "os": { + "kernel": "4.19.0-18-cloud-amd64", + "codename": "buster", + "name": "Debian GNU/Linux", + "type": "linux", + "family": "debian", + "version": "10 (buster)", + "platform": "debian" + }, + "containerized": false, + "ip": "11.200.0.196", + "name": "security-linux-3", + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:31.654Z", + "dataset": "elastic_agent.filebeat", + "kind": "signal" + }, + "service.name": "filebeat", + "message": "Status message.", + "@timestamp": "2022-03-23T16:50:51.495Z", + "data_stream": { + "namespace": "default", + "type": "logs", + "dataset": "elastic_agent.filebeat" + }, + "threat": { + "enrichments": [ + { + "indicator": {}, + "matched": { + "atomic": "security-linux-3", + "field": "host.name", + "id": "NWyvt38BIyEvspK013R3", + "index": "threat-index-000001", + "type": "indicator_match_rule" + } + } + ] + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "Pmyvt38BIyEvspK043Ri", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "Pmyvt38BIyEvspK043Ri", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "62f9a8c0-aac9-11ec-aa31-c9ea2cb79db7", + "actions": [], + "interval": "1m", + "name": "threat-match-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:47.264Z", + "updated_at": "2022-03-23T16:50:48.396Z", + "description": "a simple threat match rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "bef9b0da-8c2f-4b82-930f-37ffa1b57fc1", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "threat_match", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [], + "threat_query": "*", + "threat_mapping": [ + { + "entries": [ + { + "field": "host.name", + "type": "mapping", + "value": "host.name" + } + ] + } + ], + "threat_language": "kuery", + "threat_index": [ + "threat-index-*" + ], + "threat_indicator_path": "threat.indicator" + }, + "reason": "event on security-linux-3 created low alert threat-match-rule.", + "depth": 1, + "parent": { + "id": "Pmyvt38BIyEvspK043Ri", + "type": "event", + "index": "events-index-000001", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:31.654Z", + "original_event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:31.654Z", + "dataset": "elastic_agent.filebeat" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "999fef09ceb58f30dcbbe2a5fd410f8a22dda6179fa5f1041c7a759a31932ef9", + "source": { + "agent": { + "name": "security-linux-2.example.dev", + "id": "87c417dd-08d6-4e24-ad69-285cb8de84e9", + "type": "filebeat", + "version": "7.16.0" + }, + "log": { + "file": { + "path": "/opt/Elastic/Agent/data/elastic-agent-a13c93/logs/default/filebeat-20220301-3.ndjson" + }, + "offset": 148938 + }, + "cloud": { + "availability_zone": "us-central1-c", + "instance": { + "name": "security-linux-1", + "id": "8995531128842994872" + }, + "provider": "gcp", + "service": { + "name": "GCE" + }, + "machine": { + "type": "g1-small" + }, + "project": { + "id": "elastic-siem" + }, + "account": { + "id": "elastic-siem" + } + }, + "ecs": { + "version": "7.16.0" + }, + "host": { + "hostname": "security-linux-2", + "os": { + "kernel": "4.19.0-18-cloud-amd64", + "codename": "buster", + "name": "Debian GNU/Linux", + "type": "linux", + "family": "debian", + "version": "10 (buster)", + "platform": "debian" + }, + "containerized": false, + "ip": "11.200.0.195", + "name": "security-linux-2", + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:31.330Z", + "dataset": "elastic_agent.filebeat", + "kind": "signal" + }, + "service.name": "filebeat", + "message": "Status message.", + "@timestamp": "2022-03-23T16:50:51.496Z", + "data_stream": { + "namespace": "default", + "type": "logs", + "dataset": "elastic_agent.filebeat" + }, + "threat": { + "enrichments": [ + { + "indicator": {}, + "matched": { + "atomic": "security-linux-2", + "field": "host.name", + "id": "NGyvt38BIyEvspK01nQn", + "index": "threat-index-000001", + "type": "indicator_match_rule" + } + } + ] + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "PWyvt38BIyEvspK04XTd", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "PWyvt38BIyEvspK04XTd", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "62f9a8c0-aac9-11ec-aa31-c9ea2cb79db7", + "actions": [], + "interval": "1m", + "name": "threat-match-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:47.264Z", + "updated_at": "2022-03-23T16:50:48.396Z", + "description": "a simple threat match rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "bef9b0da-8c2f-4b82-930f-37ffa1b57fc1", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "threat_match", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [], + "threat_query": "*", + "threat_mapping": [ + { + "entries": [ + { + "field": "host.name", + "type": "mapping", + "value": "host.name" + } + ] + } + ], + "threat_language": "kuery", + "threat_index": [ + "threat-index-*" + ], + "threat_indicator_path": "threat.indicator" + }, + "reason": "event on security-linux-2 created low alert threat-match-rule.", + "depth": 1, + "parent": { + "id": "PWyvt38BIyEvspK04XTd", + "type": "event", + "index": "events-index-000001", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:31.330Z", + "original_event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:31.330Z", + "dataset": "elastic_agent.filebeat" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "f75bc411e6b0c30c26aa310c1e65ff8430cc0a98ddf74c335941dd7456858e85", + "source": { + "agent": { + "name": "security-linux-1.example.dev", + "id": "d8f66724-3cf2-437c-b124-6ac9fb0e2311", + "type": "filebeat", + "version": "7.16.0" + }, + "log": { + "file": { + "path": "/opt/Elastic/Agent/data/elastic-agent-a13c93/logs/default/filebeat-20220301-3.ndjson" + }, + "offset": 148938 + }, + "cloud": { + "availability_zone": "us-central1-c", + "instance": { + "name": "security-linux-1", + "id": "8995531128842994872" + }, + "provider": "gcp", + "service": { + "name": "GCE" + }, + "machine": { + "type": "g1-small" + }, + "project": { + "id": "elastic-siem" + }, + "account": { + "id": "elastic-siem" + } + }, + "ecs": { + "version": "7.16.0" + }, + "host": { + "hostname": "security-linux-1", + "os": { + "kernel": "4.19.0-18-cloud-amd64", + "codename": "buster", + "name": "Debian GNU/Linux", + "type": "linux", + "family": "debian", + "version": "10 (buster)", + "platform": "debian" + }, + "containerized": false, + "ip": "11.200.0.194", + "name": "security-linux-1", + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:31.001Z", + "dataset": "elastic_agent.filebeat", + "kind": "signal" + }, + "service.name": "filebeat", + "message": "Status message.", + "@timestamp": "2022-03-23T16:50:51.497Z", + "data_stream": { + "namespace": "default", + "type": "logs", + "dataset": "elastic_agent.filebeat" + }, + "threat": { + "enrichments": [ + { + "indicator": {}, + "matched": { + "atomic": "security-linux-1", + "field": "host.name", + "id": "M2yvt38BIyEvspK01XQt", + "index": "threat-index-000001", + "type": "indicator_match_rule" + } + } + ] + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "PGyvt38BIyEvspK04HSX", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "PGyvt38BIyEvspK04HSX", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "62f9a8c0-aac9-11ec-aa31-c9ea2cb79db7", + "actions": [], + "interval": "1m", + "name": "threat-match-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:47.264Z", + "updated_at": "2022-03-23T16:50:48.396Z", + "description": "a simple threat match rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "bef9b0da-8c2f-4b82-930f-37ffa1b57fc1", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "threat_match", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [], + "threat_query": "*", + "threat_mapping": [ + { + "entries": [ + { + "field": "host.name", + "type": "mapping", + "value": "host.name" + } + ] + } + ], + "threat_language": "kuery", + "threat_index": [ + "threat-index-*" + ], + "threat_indicator_path": "threat.indicator" + }, + "reason": "event on security-linux-1 created low alert threat-match-rule.", + "depth": 1, + "parent": { + "id": "PGyvt38BIyEvspK04HSX", + "type": "event", + "index": "events-index-000001", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:31.001Z", + "original_event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:31.001Z", + "dataset": "elastic_agent.filebeat" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "b46b35ce011486304a3a1e1b1dc2b772e2b80684a3a8663e9cd101691cff7429", + "source": { + "agent": { + "name": "security-linux-3.example.dev", + "id": "06851da1-73e7-41e3-97d6-ff1d62c98dc5", + "type": "filebeat", + "version": "7.16.0" + }, + "log": { + "file": { + "path": "/opt/Elastic/Agent/data/elastic-agent-a13c93/logs/default/filebeat-20220301-3.ndjson" + }, + "offset": 148938 + }, + "cloud": { + "availability_zone": "us-central1-c", + "instance": { + "name": "security-linux-1", + "id": "8995531128842994872" + }, + "provider": "gcp", + "service": { + "name": "GCE" + }, + "machine": { + "type": "g1-small" + }, + "project": { + "id": "elastic-siem" + }, + "account": { + "id": "elastic-siem" + } + }, + "ecs": { + "version": "7.16.0" + }, + "host": { + "hostname": "security-linux-3", + "os": { + "kernel": "4.19.0-18-cloud-amd64", + "codename": "buster", + "name": "Debian GNU/Linux", + "type": "linux", + "family": "debian", + "version": "10 (buster)", + "platform": "debian" + }, + "containerized": false, + "ip": "11.200.0.196", + "name": "security-linux-3", + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:30.665Z", + "dataset": "elastic_agent.filebeat", + "kind": "signal" + }, + "service.name": "filebeat", + "message": "Status message.", + "@timestamp": "2022-03-23T16:50:51.498Z", + "data_stream": { + "namespace": "default", + "type": "logs", + "dataset": "elastic_agent.filebeat" + }, + "threat": { + "enrichments": [ + { + "indicator": {}, + "matched": { + "atomic": "security-linux-3", + "field": "host.name", + "id": "NWyvt38BIyEvspK013R3", + "index": "threat-index-000001", + "type": "indicator_match_rule" + } + } + ] + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "O2yvt38BIyEvspK033RN", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "O2yvt38BIyEvspK033RN", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "62f9a8c0-aac9-11ec-aa31-c9ea2cb79db7", + "actions": [], + "interval": "1m", + "name": "threat-match-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:47.264Z", + "updated_at": "2022-03-23T16:50:48.396Z", + "description": "a simple threat match rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "bef9b0da-8c2f-4b82-930f-37ffa1b57fc1", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "threat_match", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [], + "threat_query": "*", + "threat_mapping": [ + { + "entries": [ + { + "field": "host.name", + "type": "mapping", + "value": "host.name" + } + ] + } + ], + "threat_language": "kuery", + "threat_index": [ + "threat-index-*" + ], + "threat_indicator_path": "threat.indicator" + }, + "reason": "event on security-linux-3 created low alert threat-match-rule.", + "depth": 1, + "parent": { + "id": "O2yvt38BIyEvspK033RN", + "type": "event", + "index": "events-index-000001", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:30.665Z", + "original_event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:30.665Z", + "dataset": "elastic_agent.filebeat" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "570caf7637457b9721fd46ec22166adb57916298bf68ef31df07bd0bbac95d7c", + "source": { + "agent": { + "name": "security-linux-2.example.dev", + "id": "87c417dd-08d6-4e24-ad69-285cb8de84e9", + "type": "filebeat", + "version": "7.16.0" + }, + "log": { + "file": { + "path": "/opt/Elastic/Agent/data/elastic-agent-a13c93/logs/default/filebeat-20220301-3.ndjson" + }, + "offset": 148938 + }, + "cloud": { + "availability_zone": "us-central1-c", + "instance": { + "name": "security-linux-1", + "id": "8995531128842994872" + }, + "provider": "gcp", + "service": { + "name": "GCE" + }, + "machine": { + "type": "g1-small" + }, + "project": { + "id": "elastic-siem" + }, + "account": { + "id": "elastic-siem" + } + }, + "ecs": { + "version": "7.16.0" + }, + "host": { + "hostname": "security-linux-2", + "os": { + "kernel": "4.19.0-18-cloud-amd64", + "codename": "buster", + "name": "Debian GNU/Linux", + "type": "linux", + "family": "debian", + "version": "10 (buster)", + "platform": "debian" + }, + "containerized": false, + "ip": "11.200.0.195", + "name": "security-linux-2", + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:30.353Z", + "dataset": "elastic_agent.filebeat", + "kind": "signal" + }, + "service.name": "filebeat", + "message": "Status message.", + "@timestamp": "2022-03-23T16:50:51.499Z", + "data_stream": { + "namespace": "default", + "type": "logs", + "dataset": "elastic_agent.filebeat" + }, + "threat": { + "enrichments": [ + { + "indicator": {}, + "matched": { + "atomic": "security-linux-2", + "field": "host.name", + "id": "NGyvt38BIyEvspK01nQn", + "index": "threat-index-000001", + "type": "indicator_match_rule" + } + } + ] + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "Omyvt38BIyEvspK03nQB", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "Omyvt38BIyEvspK03nQB", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "62f9a8c0-aac9-11ec-aa31-c9ea2cb79db7", + "actions": [], + "interval": "1m", + "name": "threat-match-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:47.264Z", + "updated_at": "2022-03-23T16:50:48.396Z", + "description": "a simple threat match rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "bef9b0da-8c2f-4b82-930f-37ffa1b57fc1", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "threat_match", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [], + "threat_query": "*", + "threat_mapping": [ + { + "entries": [ + { + "field": "host.name", + "type": "mapping", + "value": "host.name" + } + ] + } + ], + "threat_language": "kuery", + "threat_index": [ + "threat-index-*" + ], + "threat_indicator_path": "threat.indicator" + }, + "reason": "event on security-linux-2 created low alert threat-match-rule.", + "depth": 1, + "parent": { + "id": "Omyvt38BIyEvspK03nQB", + "type": "event", + "index": "events-index-000001", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:30.353Z", + "original_event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:30.353Z", + "dataset": "elastic_agent.filebeat" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "0c7bfb7198c9db281b639b1044c74db2b881e3152ee863e6c9304a6fb5d0e5bb", + "source": { + "agent": { + "name": "security-linux-1.example.dev", + "id": "d8f66724-3cf2-437c-b124-6ac9fb0e2311", + "type": "filebeat", + "version": "7.16.0" + }, + "log": { + "file": { + "path": "/opt/Elastic/Agent/data/elastic-agent-a13c93/logs/default/filebeat-20220301-3.ndjson" + }, + "offset": 148938 + }, + "cloud": { + "availability_zone": "us-central1-c", + "instance": { + "name": "security-linux-1", + "id": "8995531128842994872" + }, + "provider": "gcp", + "service": { + "name": "GCE" + }, + "machine": { + "type": "g1-small" + }, + "project": { + "id": "elastic-siem" + }, + "account": { + "id": "elastic-siem" + } + }, + "ecs": { + "version": "7.16.0" + }, + "host": { + "hostname": "security-linux-1", + "os": { + "kernel": "4.19.0-18-cloud-amd64", + "codename": "buster", + "name": "Debian GNU/Linux", + "type": "linux", + "family": "debian", + "version": "10 (buster)", + "platform": "debian" + }, + "containerized": false, + "ip": "11.200.0.194", + "name": "security-linux-1", + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:30.031Z", + "dataset": "elastic_agent.filebeat", + "kind": "signal" + }, + "service.name": "filebeat", + "message": "Status message.", + "@timestamp": "2022-03-23T16:50:51.501Z", + "data_stream": { + "namespace": "default", + "type": "logs", + "dataset": "elastic_agent.filebeat" + }, + "threat": { + "enrichments": [ + { + "indicator": {}, + "matched": { + "atomic": "security-linux-1", + "field": "host.name", + "id": "M2yvt38BIyEvspK01XQt", + "index": "threat-index-000001", + "type": "indicator_match_rule" + } + } + ] + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "OWyvt38BIyEvspK03HTF", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "OWyvt38BIyEvspK03HTF", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "62f9a8c0-aac9-11ec-aa31-c9ea2cb79db7", + "actions": [], + "interval": "1m", + "name": "threat-match-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:47.264Z", + "updated_at": "2022-03-23T16:50:48.396Z", + "description": "a simple threat match rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "bef9b0da-8c2f-4b82-930f-37ffa1b57fc1", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "threat_match", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [], + "threat_query": "*", + "threat_mapping": [ + { + "entries": [ + { + "field": "host.name", + "type": "mapping", + "value": "host.name" + } + ] + } + ], + "threat_language": "kuery", + "threat_index": [ + "threat-index-*" + ], + "threat_indicator_path": "threat.indicator" + }, + "reason": "event on security-linux-1 created low alert threat-match-rule.", + "depth": 1, + "parent": { + "id": "OWyvt38BIyEvspK03HTF", + "type": "event", + "index": "events-index-000001", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:30.031Z", + "original_event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:30.031Z", + "dataset": "elastic_agent.filebeat" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "ae1c6e5c7680cdc986ff52b1913e93ba2a010ea207364d4782550adf180e49ee", + "source": { + "agent": { + "name": "security-linux-3.example.dev", + "id": "06851da1-73e7-41e3-97d6-ff1d62c98dc5", + "type": "filebeat", + "version": "7.16.0" + }, + "log": { + "file": { + "path": "/opt/Elastic/Agent/data/elastic-agent-a13c93/logs/default/filebeat-20220301-3.ndjson" + }, + "offset": 148938 + }, + "cloud": { + "availability_zone": "us-central1-c", + "instance": { + "name": "security-linux-1", + "id": "8995531128842994872" + }, + "provider": "gcp", + "service": { + "name": "GCE" + }, + "machine": { + "type": "g1-small" + }, + "project": { + "id": "elastic-siem" + }, + "account": { + "id": "elastic-siem" + } + }, + "ecs": { + "version": "7.16.0" + }, + "host": { + "hostname": "security-linux-3", + "os": { + "kernel": "4.19.0-18-cloud-amd64", + "codename": "buster", + "name": "Debian GNU/Linux", + "type": "linux", + "family": "debian", + "version": "10 (buster)", + "platform": "debian" + }, + "containerized": false, + "ip": "11.200.0.196", + "name": "security-linux-3", + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:29.715Z", + "dataset": "elastic_agent.filebeat", + "kind": "signal" + }, + "service.name": "filebeat", + "message": "Status message.", + "@timestamp": "2022-03-23T16:50:51.502Z", + "data_stream": { + "namespace": "default", + "type": "logs", + "dataset": "elastic_agent.filebeat" + }, + "threat": { + "enrichments": [ + { + "indicator": {}, + "matched": { + "atomic": "security-linux-3", + "field": "host.name", + "id": "NWyvt38BIyEvspK013R3", + "index": "threat-index-000001", + "type": "indicator_match_rule" + } + } + ] + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "OGyvt38BIyEvspK023SI", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "OGyvt38BIyEvspK023SI", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "62f9a8c0-aac9-11ec-aa31-c9ea2cb79db7", + "actions": [], + "interval": "1m", + "name": "threat-match-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:47.264Z", + "updated_at": "2022-03-23T16:50:48.396Z", + "description": "a simple threat match rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "bef9b0da-8c2f-4b82-930f-37ffa1b57fc1", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "threat_match", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [], + "threat_query": "*", + "threat_mapping": [ + { + "entries": [ + { + "field": "host.name", + "type": "mapping", + "value": "host.name" + } + ] + } + ], + "threat_language": "kuery", + "threat_index": [ + "threat-index-*" + ], + "threat_indicator_path": "threat.indicator" + }, + "reason": "event on security-linux-3 created low alert threat-match-rule.", + "depth": 1, + "parent": { + "id": "OGyvt38BIyEvspK023SI", + "type": "event", + "index": "events-index-000001", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:29.715Z", + "original_event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:29.715Z", + "dataset": "elastic_agent.filebeat" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "a73fda6bdb25425c8597f63e2b87b662798ad46f195c47ac4243d9d0b9705dd8", + "source": { + "agent": { + "name": "security-linux-2.example.dev", + "id": "87c417dd-08d6-4e24-ad69-285cb8de84e9", + "type": "filebeat", + "version": "7.16.0" + }, + "log": { + "file": { + "path": "/opt/Elastic/Agent/data/elastic-agent-a13c93/logs/default/filebeat-20220301-3.ndjson" + }, + "offset": 148938 + }, + "cloud": { + "availability_zone": "us-central1-c", + "instance": { + "name": "security-linux-1", + "id": "8995531128842994872" + }, + "provider": "gcp", + "service": { + "name": "GCE" + }, + "machine": { + "type": "g1-small" + }, + "project": { + "id": "elastic-siem" + }, + "account": { + "id": "elastic-siem" + } + }, + "ecs": { + "version": "7.16.0" + }, + "host": { + "hostname": "security-linux-2", + "os": { + "kernel": "4.19.0-18-cloud-amd64", + "codename": "buster", + "name": "Debian GNU/Linux", + "type": "linux", + "family": "debian", + "version": "10 (buster)", + "platform": "debian" + }, + "containerized": false, + "ip": "11.200.0.195", + "name": "security-linux-2", + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:29.387Z", + "dataset": "elastic_agent.filebeat", + "kind": "signal" + }, + "service.name": "filebeat", + "message": "Status message.", + "@timestamp": "2022-03-23T16:50:51.503Z", + "data_stream": { + "namespace": "default", + "type": "logs", + "dataset": "elastic_agent.filebeat" + }, + "threat": { + "enrichments": [ + { + "indicator": {}, + "matched": { + "atomic": "security-linux-2", + "field": "host.name", + "id": "NGyvt38BIyEvspK01nQn", + "index": "threat-index-000001", + "type": "indicator_match_rule" + } + } + ] + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "N2yvt38BIyEvspK02nRK", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "N2yvt38BIyEvspK02nRK", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "62f9a8c0-aac9-11ec-aa31-c9ea2cb79db7", + "actions": [], + "interval": "1m", + "name": "threat-match-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:47.264Z", + "updated_at": "2022-03-23T16:50:48.396Z", + "description": "a simple threat match rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "bef9b0da-8c2f-4b82-930f-37ffa1b57fc1", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "threat_match", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [], + "threat_query": "*", + "threat_mapping": [ + { + "entries": [ + { + "field": "host.name", + "type": "mapping", + "value": "host.name" + } + ] + } + ], + "threat_language": "kuery", + "threat_index": [ + "threat-index-*" + ], + "threat_indicator_path": "threat.indicator" + }, + "reason": "event on security-linux-2 created low alert threat-match-rule.", + "depth": 1, + "parent": { + "id": "N2yvt38BIyEvspK02nRK", + "type": "event", + "index": "events-index-000001", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:29.387Z", + "original_event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:29.387Z", + "dataset": "elastic_agent.filebeat" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "77038fe81327ce7b578e69896fdd1869fab16d13633b5fb0cb7743bae9120ca5", + "source": { + "agent": { + "name": "security-linux-1.example.dev", + "id": "d8f66724-3cf2-437c-b124-6ac9fb0e2311", + "type": "filebeat", + "version": "7.16.0" + }, + "log": { + "file": { + "path": "/opt/Elastic/Agent/data/elastic-agent-a13c93/logs/default/filebeat-20220301-3.ndjson" + }, + "offset": 148938 + }, + "cloud": { + "availability_zone": "us-central1-c", + "instance": { + "name": "security-linux-1", + "id": "8995531128842994872" + }, + "provider": "gcp", + "service": { + "name": "GCE" + }, + "machine": { + "type": "g1-small" + }, + "project": { + "id": "elastic-siem" + }, + "account": { + "id": "elastic-siem" + } + }, + "ecs": { + "version": "7.16.0" + }, + "host": { + "hostname": "security-linux-1", + "os": { + "kernel": "4.19.0-18-cloud-amd64", + "codename": "buster", + "name": "Debian GNU/Linux", + "type": "linux", + "family": "debian", + "version": "10 (buster)", + "platform": "debian" + }, + "containerized": false, + "ip": "11.200.0.194", + "name": "security-linux-1", + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:28.994Z", + "dataset": "elastic_agent.filebeat", + "kind": "signal" + }, + "service.name": "filebeat", + "message": "Status message.", + "@timestamp": "2022-03-23T16:50:51.504Z", + "data_stream": { + "namespace": "default", + "type": "logs", + "dataset": "elastic_agent.filebeat" + }, + "threat": { + "enrichments": [ + { + "indicator": {}, + "matched": { + "atomic": "security-linux-1", + "field": "host.name", + "id": "M2yvt38BIyEvspK01XQt", + "index": "threat-index-000001", + "type": "indicator_match_rule" + } + } + ] + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "Nmyvt38BIyEvspK02HTJ", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "Nmyvt38BIyEvspK02HTJ", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "62f9a8c0-aac9-11ec-aa31-c9ea2cb79db7", + "actions": [], + "interval": "1m", + "name": "threat-match-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:47.264Z", + "updated_at": "2022-03-23T16:50:48.396Z", + "description": "a simple threat match rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "bef9b0da-8c2f-4b82-930f-37ffa1b57fc1", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "threat_match", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [], + "threat_query": "*", + "threat_mapping": [ + { + "entries": [ + { + "field": "host.name", + "type": "mapping", + "value": "host.name" + } + ] + } + ], + "threat_language": "kuery", + "threat_index": [ + "threat-index-*" + ], + "threat_indicator_path": "threat.indicator" + }, + "reason": "event on security-linux-1 created low alert threat-match-rule.", + "depth": 1, + "parent": { + "id": "Nmyvt38BIyEvspK02HTJ", + "type": "event", + "index": "events-index-000001", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:28.994Z", + "original_event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:28.994Z", + "dataset": "elastic_agent.filebeat" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "21d26a1ad7b01b28667638d5f8db96f6e94957394efe7a16057948095a445ac4", + "source": { + "@timestamp": "2022-03-23T16:50:48.441Z", + "host.name": "security-linux-1", + "event": { + "kind": "signal" + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "8e75aa13-6b35-5d96-b52b-1d62909a9d75", + "type": "event", + "index": "events-index-*", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "8e75aa13-6b35-5d96-b52b-1d62909a9d75", + "type": "event", + "index": "events-index-*", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "60b8b970-aac9-11ec-bb53-fd375b7a173a", + "actions": [], + "interval": "1m", + "name": "threshold-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:44.260Z", + "updated_at": "2022-03-23T16:50:45.341Z", + "description": "a simple threshold rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "b97ae2a4-f188-43b2-b082-69667b563152", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "threshold", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [], + "threshold": { + "field": [ + "host.name" + ], + "value": 1 + } + }, + "reason": "event created low alert threshold-rule.", + "depth": 1, + "parent": { + "id": "8e75aa13-6b35-5d96-b52b-1d62909a9d75", + "type": "event", + "index": "events-index-*", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:32.045Z", + "threshold_result": { + "terms": [ + { + "field": "host.name", + "value": "security-linux-1" + } + ], + "count": 4, + "from": "2022-03-23T06:50:48.395Z" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "4c2a3865ca7df72e4cc17b5114feb2535b2459fd52f6fbd0669d4884f5956dc2", + "source": { + "@timestamp": "2022-03-23T16:50:48.442Z", + "host.name": "security-linux-2", + "event": { + "kind": "signal" + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "9c957c24-8ce5-516b-ba8e-44b582da6579", + "type": "event", + "index": "events-index-*", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "9c957c24-8ce5-516b-ba8e-44b582da6579", + "type": "event", + "index": "events-index-*", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "60b8b970-aac9-11ec-bb53-fd375b7a173a", + "actions": [], + "interval": "1m", + "name": "threshold-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:44.260Z", + "updated_at": "2022-03-23T16:50:45.341Z", + "description": "a simple threshold rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "b97ae2a4-f188-43b2-b082-69667b563152", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "threshold", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [], + "threshold": { + "field": [ + "host.name" + ], + "value": 1 + } + }, + "reason": "event created low alert threshold-rule.", + "depth": 1, + "parent": { + "id": "9c957c24-8ce5-516b-ba8e-44b582da6579", + "type": "event", + "index": "events-index-*", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:31.330Z", + "threshold_result": { + "terms": [ + { + "field": "host.name", + "value": "security-linux-2" + } + ], + "count": 3, + "from": "2022-03-23T06:50:48.395Z" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "3754896311b1d9f9dee45ecf06aa5160f8cd3d4504ef5c856ba285edd61d059d", + "source": { + "@timestamp": "2022-03-23T16:50:48.442Z", + "host.name": "security-linux-3", + "event": { + "kind": "signal" + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "326cc81c-b55f-5b69-8222-e930bcb24692", + "type": "event", + "index": "events-index-*", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "326cc81c-b55f-5b69-8222-e930bcb24692", + "type": "event", + "index": "events-index-*", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "60b8b970-aac9-11ec-bb53-fd375b7a173a", + "actions": [], + "interval": "1m", + "name": "threshold-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:44.260Z", + "updated_at": "2022-03-23T16:50:45.341Z", + "description": "a simple threshold rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "b97ae2a4-f188-43b2-b082-69667b563152", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "threshold", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [], + "threshold": { + "field": [ + "host.name" + ], + "value": 1 + } + }, + "reason": "event created low alert threshold-rule.", + "depth": 1, + "parent": { + "id": "326cc81c-b55f-5b69-8222-e930bcb24692", + "type": "event", + "index": "events-index-*", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:31.654Z", + "threshold_result": { + "terms": [ + { + "field": "host.name", + "value": "security-linux-3" + } + ], + "count": 3, + "from": "2022-03-23T06:50:48.395Z" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "5cddda6852c5f8b6c32d4bfa5e876aa51884e0c7a2d4faaababf91ec9cb68de7", + "source": { + "agent": { + "name": "security-linux-1.example.dev", + "id": "d8f66724-3cf2-437c-b124-6ac9fb0e2311", + "type": "filebeat", + "version": "7.16.0" + }, + "log": { + "file": { + "path": "/opt/Elastic/Agent/data/elastic-agent-a13c93/logs/default/filebeat-20220301-3.ndjson" + }, + "offset": 148938 + }, + "cloud": { + "availability_zone": "us-central1-c", + "instance": { + "name": "security-linux-1", + "id": "8995531128842994872" + }, + "provider": "gcp", + "service": { + "name": "GCE" + }, + "machine": { + "type": "g1-small" + }, + "project": { + "id": "elastic-siem" + }, + "account": { + "id": "elastic-siem" + } + }, + "ecs": { + "version": "7.16.0" + }, + "host": { + "hostname": "security-linux-1", + "os": { + "kernel": "4.19.0-18-cloud-amd64", + "codename": "buster", + "name": "Debian GNU/Linux", + "type": "linux", + "family": "debian", + "version": "10 (buster)", + "platform": "debian" + }, + "containerized": false, + "ip": "11.200.0.194", + "name": "security-linux-1", + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:28.994Z", + "dataset": "elastic_agent.filebeat", + "kind": "signal" + }, + "service.name": "filebeat", + "message": "Status message.", + "@timestamp": "2022-03-23T16:50:40.440Z", + "data_stream": { + "namespace": "default", + "type": "logs", + "dataset": "elastic_agent.filebeat" + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "Nmyvt38BIyEvspK02HTJ", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "Nmyvt38BIyEvspK02HTJ", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "5b7cd9a0-aac9-11ec-bb53-fd375b7a173a", + "actions": [], + "interval": "1m", + "name": "query-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:34.234Z", + "updated_at": "2022-03-23T16:50:36.214Z", + "description": "a simple query rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "1fcc46ae-7e1e-4002-a4e1-e456029cb7ec", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "query", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [] + }, + "reason": "event on security-linux-1 created low alert query-rule.", + "depth": 1, + "parent": { + "id": "Nmyvt38BIyEvspK02HTJ", + "type": "event", + "index": "events-index-000001", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:28.994Z", + "original_event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:28.994Z", + "dataset": "elastic_agent.filebeat" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "5050902fa762858249c32b1d228dd71ca9217ace612b65f9669fb3a5f371ab63", + "source": { + "agent": { + "name": "security-linux-2.example.dev", + "id": "87c417dd-08d6-4e24-ad69-285cb8de84e9", + "type": "filebeat", + "version": "7.16.0" + }, + "log": { + "file": { + "path": "/opt/Elastic/Agent/data/elastic-agent-a13c93/logs/default/filebeat-20220301-3.ndjson" + }, + "offset": 148938 + }, + "cloud": { + "availability_zone": "us-central1-c", + "instance": { + "name": "security-linux-1", + "id": "8995531128842994872" + }, + "provider": "gcp", + "service": { + "name": "GCE" + }, + "machine": { + "type": "g1-small" + }, + "project": { + "id": "elastic-siem" + }, + "account": { + "id": "elastic-siem" + } + }, + "ecs": { + "version": "7.16.0" + }, + "host": { + "hostname": "security-linux-2", + "os": { + "kernel": "4.19.0-18-cloud-amd64", + "codename": "buster", + "name": "Debian GNU/Linux", + "type": "linux", + "family": "debian", + "version": "10 (buster)", + "platform": "debian" + }, + "containerized": false, + "ip": "11.200.0.195", + "name": "security-linux-2", + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:29.387Z", + "dataset": "elastic_agent.filebeat", + "kind": "signal" + }, + "service.name": "filebeat", + "message": "Status message.", + "@timestamp": "2022-03-23T16:50:40.477Z", + "data_stream": { + "namespace": "default", + "type": "logs", + "dataset": "elastic_agent.filebeat" + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "N2yvt38BIyEvspK02nRK", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "N2yvt38BIyEvspK02nRK", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "5b7cd9a0-aac9-11ec-bb53-fd375b7a173a", + "actions": [], + "interval": "1m", + "name": "query-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:34.234Z", + "updated_at": "2022-03-23T16:50:36.214Z", + "description": "a simple query rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "1fcc46ae-7e1e-4002-a4e1-e456029cb7ec", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "query", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [] + }, + "reason": "event on security-linux-2 created low alert query-rule.", + "depth": 1, + "parent": { + "id": "N2yvt38BIyEvspK02nRK", + "type": "event", + "index": "events-index-000001", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:29.387Z", + "original_event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:29.387Z", + "dataset": "elastic_agent.filebeat" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "525833fe5aa3cabce849adf9291b4d4009c25edbe528d5d2add1dc749c00513b", + "source": { + "agent": { + "name": "security-linux-3.example.dev", + "id": "06851da1-73e7-41e3-97d6-ff1d62c98dc5", + "type": "filebeat", + "version": "7.16.0" + }, + "log": { + "file": { + "path": "/opt/Elastic/Agent/data/elastic-agent-a13c93/logs/default/filebeat-20220301-3.ndjson" + }, + "offset": 148938 + }, + "cloud": { + "availability_zone": "us-central1-c", + "instance": { + "name": "security-linux-1", + "id": "8995531128842994872" + }, + "provider": "gcp", + "service": { + "name": "GCE" + }, + "machine": { + "type": "g1-small" + }, + "project": { + "id": "elastic-siem" + }, + "account": { + "id": "elastic-siem" + } + }, + "ecs": { + "version": "7.16.0" + }, + "host": { + "hostname": "security-linux-3", + "os": { + "kernel": "4.19.0-18-cloud-amd64", + "codename": "buster", + "name": "Debian GNU/Linux", + "type": "linux", + "family": "debian", + "version": "10 (buster)", + "platform": "debian" + }, + "containerized": false, + "ip": "11.200.0.196", + "name": "security-linux-3", + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:29.715Z", + "dataset": "elastic_agent.filebeat", + "kind": "signal" + }, + "service.name": "filebeat", + "message": "Status message.", + "@timestamp": "2022-03-23T16:50:40.499Z", + "data_stream": { + "namespace": "default", + "type": "logs", + "dataset": "elastic_agent.filebeat" + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "OGyvt38BIyEvspK023SI", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "OGyvt38BIyEvspK023SI", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "5b7cd9a0-aac9-11ec-bb53-fd375b7a173a", + "actions": [], + "interval": "1m", + "name": "query-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:34.234Z", + "updated_at": "2022-03-23T16:50:36.214Z", + "description": "a simple query rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "1fcc46ae-7e1e-4002-a4e1-e456029cb7ec", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "query", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [] + }, + "reason": "event on security-linux-3 created low alert query-rule.", + "depth": 1, + "parent": { + "id": "OGyvt38BIyEvspK023SI", + "type": "event", + "index": "events-index-000001", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:29.715Z", + "original_event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:29.715Z", + "dataset": "elastic_agent.filebeat" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "4f9c5a7581544f9dc1fa4c9f541c7e7573d7460ddeeda1875bee081e6615035b", + "source": { + "agent": { + "name": "security-linux-1.example.dev", + "id": "d8f66724-3cf2-437c-b124-6ac9fb0e2311", + "type": "filebeat", + "version": "7.16.0" + }, + "log": { + "file": { + "path": "/opt/Elastic/Agent/data/elastic-agent-a13c93/logs/default/filebeat-20220301-3.ndjson" + }, + "offset": 148938 + }, + "cloud": { + "availability_zone": "us-central1-c", + "instance": { + "name": "security-linux-1", + "id": "8995531128842994872" + }, + "provider": "gcp", + "service": { + "name": "GCE" + }, + "machine": { + "type": "g1-small" + }, + "project": { + "id": "elastic-siem" + }, + "account": { + "id": "elastic-siem" + } + }, + "ecs": { + "version": "7.16.0" + }, + "host": { + "hostname": "security-linux-1", + "os": { + "kernel": "4.19.0-18-cloud-amd64", + "codename": "buster", + "name": "Debian GNU/Linux", + "type": "linux", + "family": "debian", + "version": "10 (buster)", + "platform": "debian" + }, + "containerized": false, + "ip": "11.200.0.194", + "name": "security-linux-1", + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:30.031Z", + "dataset": "elastic_agent.filebeat", + "kind": "signal" + }, + "service.name": "filebeat", + "message": "Status message.", + "@timestamp": "2022-03-23T16:50:40.510Z", + "data_stream": { + "namespace": "default", + "type": "logs", + "dataset": "elastic_agent.filebeat" + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "OWyvt38BIyEvspK03HTF", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "OWyvt38BIyEvspK03HTF", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "5b7cd9a0-aac9-11ec-bb53-fd375b7a173a", + "actions": [], + "interval": "1m", + "name": "query-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:34.234Z", + "updated_at": "2022-03-23T16:50:36.214Z", + "description": "a simple query rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "1fcc46ae-7e1e-4002-a4e1-e456029cb7ec", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "query", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [] + }, + "reason": "event on security-linux-1 created low alert query-rule.", + "depth": 1, + "parent": { + "id": "OWyvt38BIyEvspK03HTF", + "type": "event", + "index": "events-index-000001", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:30.031Z", + "original_event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:30.031Z", + "dataset": "elastic_agent.filebeat" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "d791d45b87a37e3b8a8388d7d6237728aa14ab6ec81bfa84f96457bd42b39e4a", + "source": { + "agent": { + "name": "security-linux-2.example.dev", + "id": "87c417dd-08d6-4e24-ad69-285cb8de84e9", + "type": "filebeat", + "version": "7.16.0" + }, + "log": { + "file": { + "path": "/opt/Elastic/Agent/data/elastic-agent-a13c93/logs/default/filebeat-20220301-3.ndjson" + }, + "offset": 148938 + }, + "cloud": { + "availability_zone": "us-central1-c", + "instance": { + "name": "security-linux-1", + "id": "8995531128842994872" + }, + "provider": "gcp", + "service": { + "name": "GCE" + }, + "machine": { + "type": "g1-small" + }, + "project": { + "id": "elastic-siem" + }, + "account": { + "id": "elastic-siem" + } + }, + "ecs": { + "version": "7.16.0" + }, + "host": { + "hostname": "security-linux-2", + "os": { + "kernel": "4.19.0-18-cloud-amd64", + "codename": "buster", + "name": "Debian GNU/Linux", + "type": "linux", + "family": "debian", + "version": "10 (buster)", + "platform": "debian" + }, + "containerized": false, + "ip": "11.200.0.195", + "name": "security-linux-2", + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:30.353Z", + "dataset": "elastic_agent.filebeat", + "kind": "signal" + }, + "service.name": "filebeat", + "message": "Status message.", + "@timestamp": "2022-03-23T16:50:40.533Z", + "data_stream": { + "namespace": "default", + "type": "logs", + "dataset": "elastic_agent.filebeat" + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "Omyvt38BIyEvspK03nQB", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "Omyvt38BIyEvspK03nQB", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "5b7cd9a0-aac9-11ec-bb53-fd375b7a173a", + "actions": [], + "interval": "1m", + "name": "query-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:34.234Z", + "updated_at": "2022-03-23T16:50:36.214Z", + "description": "a simple query rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "1fcc46ae-7e1e-4002-a4e1-e456029cb7ec", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "query", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [] + }, + "reason": "event on security-linux-2 created low alert query-rule.", + "depth": 1, + "parent": { + "id": "Omyvt38BIyEvspK03nQB", + "type": "event", + "index": "events-index-000001", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:30.353Z", + "original_event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:30.353Z", + "dataset": "elastic_agent.filebeat" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "747a4cfd4dbc1dd3924b341b0d3d94098252579354bf140e1621cb4b8681e911", + "source": { + "agent": { + "name": "security-linux-3.example.dev", + "id": "06851da1-73e7-41e3-97d6-ff1d62c98dc5", + "type": "filebeat", + "version": "7.16.0" + }, + "log": { + "file": { + "path": "/opt/Elastic/Agent/data/elastic-agent-a13c93/logs/default/filebeat-20220301-3.ndjson" + }, + "offset": 148938 + }, + "cloud": { + "availability_zone": "us-central1-c", + "instance": { + "name": "security-linux-1", + "id": "8995531128842994872" + }, + "provider": "gcp", + "service": { + "name": "GCE" + }, + "machine": { + "type": "g1-small" + }, + "project": { + "id": "elastic-siem" + }, + "account": { + "id": "elastic-siem" + } + }, + "ecs": { + "version": "7.16.0" + }, + "host": { + "hostname": "security-linux-3", + "os": { + "kernel": "4.19.0-18-cloud-amd64", + "codename": "buster", + "name": "Debian GNU/Linux", + "type": "linux", + "family": "debian", + "version": "10 (buster)", + "platform": "debian" + }, + "containerized": false, + "ip": "11.200.0.196", + "name": "security-linux-3", + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:30.665Z", + "dataset": "elastic_agent.filebeat", + "kind": "signal" + }, + "service.name": "filebeat", + "message": "Status message.", + "@timestamp": "2022-03-23T16:50:40.547Z", + "data_stream": { + "namespace": "default", + "type": "logs", + "dataset": "elastic_agent.filebeat" + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "O2yvt38BIyEvspK033RN", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "O2yvt38BIyEvspK033RN", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "5b7cd9a0-aac9-11ec-bb53-fd375b7a173a", + "actions": [], + "interval": "1m", + "name": "query-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:34.234Z", + "updated_at": "2022-03-23T16:50:36.214Z", + "description": "a simple query rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "1fcc46ae-7e1e-4002-a4e1-e456029cb7ec", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "query", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [] + }, + "reason": "event on security-linux-3 created low alert query-rule.", + "depth": 1, + "parent": { + "id": "O2yvt38BIyEvspK033RN", + "type": "event", + "index": "events-index-000001", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:30.665Z", + "original_event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:30.665Z", + "dataset": "elastic_agent.filebeat" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "5a217bc36610a820dbbb20f7b189065d631038a9dbb33bde1511f0f6a63183d2", + "source": { + "agent": { + "name": "security-linux-1.example.dev", + "id": "d8f66724-3cf2-437c-b124-6ac9fb0e2311", + "type": "filebeat", + "version": "7.16.0" + }, + "log": { + "file": { + "path": "/opt/Elastic/Agent/data/elastic-agent-a13c93/logs/default/filebeat-20220301-3.ndjson" + }, + "offset": 148938 + }, + "cloud": { + "availability_zone": "us-central1-c", + "instance": { + "name": "security-linux-1", + "id": "8995531128842994872" + }, + "provider": "gcp", + "service": { + "name": "GCE" + }, + "machine": { + "type": "g1-small" + }, + "project": { + "id": "elastic-siem" + }, + "account": { + "id": "elastic-siem" + } + }, + "ecs": { + "version": "7.16.0" + }, + "host": { + "hostname": "security-linux-1", + "os": { + "kernel": "4.19.0-18-cloud-amd64", + "codename": "buster", + "name": "Debian GNU/Linux", + "type": "linux", + "family": "debian", + "version": "10 (buster)", + "platform": "debian" + }, + "containerized": false, + "ip": "11.200.0.194", + "name": "security-linux-1", + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:31.001Z", + "dataset": "elastic_agent.filebeat", + "kind": "signal" + }, + "service.name": "filebeat", + "message": "Status message.", + "@timestamp": "2022-03-23T16:50:40.561Z", + "data_stream": { + "namespace": "default", + "type": "logs", + "dataset": "elastic_agent.filebeat" + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "PGyvt38BIyEvspK04HSX", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "PGyvt38BIyEvspK04HSX", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "5b7cd9a0-aac9-11ec-bb53-fd375b7a173a", + "actions": [], + "interval": "1m", + "name": "query-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:34.234Z", + "updated_at": "2022-03-23T16:50:36.214Z", + "description": "a simple query rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "1fcc46ae-7e1e-4002-a4e1-e456029cb7ec", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "query", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [] + }, + "reason": "event on security-linux-1 created low alert query-rule.", + "depth": 1, + "parent": { + "id": "PGyvt38BIyEvspK04HSX", + "type": "event", + "index": "events-index-000001", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:31.001Z", + "original_event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:31.001Z", + "dataset": "elastic_agent.filebeat" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "fde1f09c4420ce5747f04ca051bcdc90762394ea019a7cc2cfee8de3bd575a59", + "source": { + "agent": { + "name": "security-linux-2.example.dev", + "id": "87c417dd-08d6-4e24-ad69-285cb8de84e9", + "type": "filebeat", + "version": "7.16.0" + }, + "log": { + "file": { + "path": "/opt/Elastic/Agent/data/elastic-agent-a13c93/logs/default/filebeat-20220301-3.ndjson" + }, + "offset": 148938 + }, + "cloud": { + "availability_zone": "us-central1-c", + "instance": { + "name": "security-linux-1", + "id": "8995531128842994872" + }, + "provider": "gcp", + "service": { + "name": "GCE" + }, + "machine": { + "type": "g1-small" + }, + "project": { + "id": "elastic-siem" + }, + "account": { + "id": "elastic-siem" + } + }, + "ecs": { + "version": "7.16.0" + }, + "host": { + "hostname": "security-linux-2", + "os": { + "kernel": "4.19.0-18-cloud-amd64", + "codename": "buster", + "name": "Debian GNU/Linux", + "type": "linux", + "family": "debian", + "version": "10 (buster)", + "platform": "debian" + }, + "containerized": false, + "ip": "11.200.0.195", + "name": "security-linux-2", + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:31.330Z", + "dataset": "elastic_agent.filebeat", + "kind": "signal" + }, + "service.name": "filebeat", + "message": "Status message.", + "@timestamp": "2022-03-23T16:50:40.593Z", + "data_stream": { + "namespace": "default", + "type": "logs", + "dataset": "elastic_agent.filebeat" + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "PWyvt38BIyEvspK04XTd", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "PWyvt38BIyEvspK04XTd", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "5b7cd9a0-aac9-11ec-bb53-fd375b7a173a", + "actions": [], + "interval": "1m", + "name": "query-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:34.234Z", + "updated_at": "2022-03-23T16:50:36.214Z", + "description": "a simple query rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "1fcc46ae-7e1e-4002-a4e1-e456029cb7ec", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "query", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [] + }, + "reason": "event on security-linux-2 created low alert query-rule.", + "depth": 1, + "parent": { + "id": "PWyvt38BIyEvspK04XTd", + "type": "event", + "index": "events-index-000001", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:31.330Z", + "original_event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:31.330Z", + "dataset": "elastic_agent.filebeat" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "337f39b1fb862a4c6910605b16e6b5b59623219e99dcb7d442cd334229ad3a7e", + "source": { + "agent": { + "name": "security-linux-3.example.dev", + "id": "06851da1-73e7-41e3-97d6-ff1d62c98dc5", + "type": "filebeat", + "version": "7.16.0" + }, + "log": { + "file": { + "path": "/opt/Elastic/Agent/data/elastic-agent-a13c93/logs/default/filebeat-20220301-3.ndjson" + }, + "offset": 148938 + }, + "cloud": { + "availability_zone": "us-central1-c", + "instance": { + "name": "security-linux-1", + "id": "8995531128842994872" + }, + "provider": "gcp", + "service": { + "name": "GCE" + }, + "machine": { + "type": "g1-small" + }, + "project": { + "id": "elastic-siem" + }, + "account": { + "id": "elastic-siem" + } + }, + "ecs": { + "version": "7.16.0" + }, + "host": { + "hostname": "security-linux-3", + "os": { + "kernel": "4.19.0-18-cloud-amd64", + "codename": "buster", + "name": "Debian GNU/Linux", + "type": "linux", + "family": "debian", + "version": "10 (buster)", + "platform": "debian" + }, + "containerized": false, + "ip": "11.200.0.196", + "name": "security-linux-3", + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:31.654Z", + "dataset": "elastic_agent.filebeat", + "kind": "signal" + }, + "service.name": "filebeat", + "message": "Status message.", + "@timestamp": "2022-03-23T16:50:40.606Z", + "data_stream": { + "namespace": "default", + "type": "logs", + "dataset": "elastic_agent.filebeat" + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "Pmyvt38BIyEvspK043Ri", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "Pmyvt38BIyEvspK043Ri", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "5b7cd9a0-aac9-11ec-bb53-fd375b7a173a", + "actions": [], + "interval": "1m", + "name": "query-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:34.234Z", + "updated_at": "2022-03-23T16:50:36.214Z", + "description": "a simple query rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "1fcc46ae-7e1e-4002-a4e1-e456029cb7ec", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "query", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [] + }, + "reason": "event on security-linux-3 created low alert query-rule.", + "depth": 1, + "parent": { + "id": "Pmyvt38BIyEvspK043Ri", + "type": "event", + "index": "events-index-000001", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:31.654Z", + "original_event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:31.654Z", + "dataset": "elastic_agent.filebeat" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".siem-signals-default-000001-7.16.0", + "id": "44f8d6e34631ced611f6588e7f0cdf52ac5647eff09cfbd36a38ad2a7d4bf32f", + "source": { + "agent": { + "name": "security-linux-1.example.dev", + "id": "d8f66724-3cf2-437c-b124-6ac9fb0e2311", + "type": "filebeat", + "version": "7.16.0" + }, + "log": { + "file": { + "path": "/opt/Elastic/Agent/data/elastic-agent-a13c93/logs/default/filebeat-20220301-3.ndjson" + }, + "offset": 148938 + }, + "cloud": { + "availability_zone": "us-central1-c", + "instance": { + "name": "security-linux-1", + "id": "8995531128842994872" + }, + "provider": "gcp", + "service": { + "name": "GCE" + }, + "machine": { + "type": "g1-small" + }, + "project": { + "id": "elastic-siem" + }, + "account": { + "id": "elastic-siem" + } + }, + "ecs": { + "version": "7.16.0" + }, + "host": { + "hostname": "security-linux-1", + "os": { + "kernel": "4.19.0-18-cloud-amd64", + "codename": "buster", + "name": "Debian GNU/Linux", + "type": "linux", + "family": "debian", + "version": "10 (buster)", + "platform": "debian" + }, + "containerized": false, + "ip": "11.200.0.194", + "name": "security-linux-1", + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:32.045Z", + "dataset": "elastic_agent.filebeat", + "kind": "signal" + }, + "service.name": "filebeat", + "message": "Status message.", + "@timestamp": "2022-03-23T16:50:40.624Z", + "data_stream": { + "namespace": "default", + "type": "logs", + "dataset": "elastic_agent.filebeat" + }, + "signal": { + "_meta": { + "version": 57 + }, + "parents": [ + { + "id": "P2yvt38BIyEvspK05HSe", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "ancestors": [ + { + "id": "P2yvt38BIyEvspK05HSe", + "type": "event", + "index": "events-index-000001", + "depth": 0 + } + ], + "status": "open", + "rule": { + "id": "5b7cd9a0-aac9-11ec-bb53-fd375b7a173a", + "actions": [], + "interval": "1m", + "name": "query-rule", + "tags": [], + "enabled": true, + "created_by": "elastic", + "updated_by": "elastic", + "throttle": null, + "created_at": "2022-03-23T16:50:34.234Z", + "updated_at": "2022-03-23T16:50:36.214Z", + "description": "a simple query rule", + "risk_score": 21, + "severity": "low", + "license": "", + "output_index": ".siem-signals-default-000001", + "author": [], + "false_positives": [], + "from": "now-36000s", + "rule_id": "1fcc46ae-7e1e-4002-a4e1-e456029cb7ec", + "max_signals": 100, + "risk_score_mapping": [], + "severity_mapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptions_list": [], + "immutable": false, + "type": "query", + "language": "kuery", + "index": [ + "events-index-*" + ], + "query": "*", + "filters": [] + }, + "reason": "event on security-linux-1 created low alert query-rule.", + "depth": 1, + "parent": { + "id": "P2yvt38BIyEvspK05HSe", + "type": "event", + "index": "events-index-000001", + "depth": 0 + }, + "original_time": "2022-03-23T16:50:32.045Z", + "original_event": { + "agent_id_status": "verified", + "ingested": "2022-03-23T16:50:32.045Z", + "dataset": "elastic_agent.filebeat" + } + } + } + } +} + diff --git a/x-pack/test/functional/es_archives/security_solution/alerts/7.16.0/mappings.json b/x-pack/test/functional/es_archives/security_solution/alerts/7.16.0/mappings.json new file mode 100644 index 0000000000000..3838e29ee5aa4 --- /dev/null +++ b/x-pack/test/functional/es_archives/security_solution/alerts/7.16.0/mappings.json @@ -0,0 +1,5819 @@ +{ + "type": "index", + "value": { + "aliases": { + ".siem-signals-default": { + "is_write_index": true + } + }, + "index": ".siem-signals-default-000001-7.16.0", + "mappings": { + "dynamic": "false", + "_meta": { + "version": 57, + "aliases_version": 1 + }, + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "build": { + "properties": { + "original": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "ephemeral_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + } + } + } + } + }, + "client": { + "properties": { + "address": { + "type": "keyword", + "ignore_above": 1024 + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "geo": { + "properties": { + "city_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "continent_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "continent_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "location": { + "type": "geo_point" + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "postal_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "region_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "region_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "timezone": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "type": "keyword", + "ignore_above": 1024 + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "subdomain": { + "type": "keyword", + "ignore_above": 1024 + }, + "top_level_domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "user": { + "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "email": { + "type": "keyword", + "ignore_above": 1024 + }, + "full_name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "group": { + "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "hash": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "roles": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "cloud": { + "properties": { + "account": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "availability_zone": { + "type": "keyword", + "ignore_above": 1024 + }, + "instance": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "machine": { + "properties": { + "type": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "project": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "provider": { + "type": "keyword", + "ignore_above": 1024 + }, + "region": { + "type": "keyword", + "ignore_above": 1024 + }, + "service": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "type": "keyword", + "ignore_above": 1024 + }, + "subject_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "container": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "image": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "tag": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "labels": { + "type": "object" + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "runtime": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "data_stream": { + "properties": { + "dataset": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "address": { + "type": "keyword", + "ignore_above": 1024 + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "geo": { + "properties": { + "city_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "continent_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "continent_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "location": { + "type": "geo_point" + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "postal_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "region_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "region_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "timezone": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "type": "keyword", + "ignore_above": 1024 + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "subdomain": { + "type": "keyword", + "ignore_above": 1024 + }, + "top_level_domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "user": { + "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "email": { + "type": "keyword", + "ignore_above": 1024 + }, + "full_name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "group": { + "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "hash": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "roles": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "dll": { + "properties": { + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "signing_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "status": { + "type": "keyword", + "ignore_above": 1024 + }, + "subject_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "team_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "hash": { + "properties": { + "md5": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha1": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha256": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha512": { + "type": "keyword", + "ignore_above": 1024 + }, + "ssdeep": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "path": { + "type": "keyword", + "ignore_above": 1024 + }, + "pe": { + "properties": { + "architecture": { + "type": "keyword", + "ignore_above": 1024 + }, + "company": { + "type": "keyword", + "ignore_above": 1024 + }, + "description": { + "type": "keyword", + "ignore_above": 1024 + }, + "file_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "imphash": { + "type": "keyword", + "ignore_above": 1024 + }, + "original_file_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "product": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "dns": { + "properties": { + "answers": { + "properties": { + "class": { + "type": "keyword", + "ignore_above": 1024 + }, + "data": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "ttl": { + "type": "long" + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "header_flags": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "op_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "question": { + "properties": { + "class": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "registered_domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "subdomain": { + "type": "keyword", + "ignore_above": 1024 + }, + "top_level_domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "resolved_ip": { + "type": "ip" + }, + "response_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "ecs": { + "properties": { + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "error": { + "properties": { + "code": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "message": { + "type": "text", + "norms": false + }, + "stack_trace": { + "type": "keyword", + "index": false, + "doc_values": false, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "event": { + "properties": { + "action": { + "type": "keyword", + "ignore_above": 1024 + }, + "agent_id_status": { + "type": "keyword", + "ignore_above": 1024 + }, + "category": { + "type": "keyword", + "ignore_above": 1024 + }, + "code": { + "type": "keyword", + "ignore_above": 1024 + }, + "created": { + "type": "date" + }, + "dataset": { + "type": "keyword", + "ignore_above": 1024 + }, + "duration": { + "type": "long" + }, + "end": { + "type": "date" + }, + "hash": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "ingested": { + "type": "date" + }, + "kind": { + "type": "keyword", + "ignore_above": 1024 + }, + "module": { + "type": "keyword", + "ignore_above": 1024 + }, + "original": { + "type": "keyword", + "index": false, + "doc_values": false + }, + "outcome": { + "type": "keyword", + "ignore_above": 1024 + }, + "provider": { + "type": "keyword", + "ignore_above": 1024 + }, + "reason": { + "type": "keyword", + "ignore_above": 1024 + }, + "reference": { + "type": "keyword", + "ignore_above": 1024 + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "url": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "type": "keyword", + "ignore_above": 1024 + }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "signing_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "status": { + "type": "keyword", + "ignore_above": 1024 + }, + "subject_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "team_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "type": "keyword", + "ignore_above": 1024 + }, + "directory": { + "type": "keyword", + "ignore_above": 1024 + }, + "drive_letter": { + "type": "keyword", + "ignore_above": 1 + }, + "elf": { + "properties": { + "architecture": { + "type": "keyword", + "ignore_above": 1024 + }, + "byte_order": { + "type": "keyword", + "ignore_above": 1024 + }, + "cpu_type": { + "type": "keyword", + "ignore_above": 1024 + }, + "creation_date": { + "type": "date" + }, + "exports": { + "type": "flattened" + }, + "header": { + "properties": { + "abi_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "class": { + "type": "keyword", + "ignore_above": 1024 + }, + "data": { + "type": "keyword", + "ignore_above": 1024 + }, + "entrypoint": { + "type": "long" + }, + "object_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "os_abi": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "imports": { + "type": "flattened" + }, + "sections": { + "type": "nested", + "properties": { + "chi2": { + "type": "long" + }, + "entropy": { + "type": "long" + }, + "flags": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "physical_offset": { + "type": "keyword", + "ignore_above": 1024 + }, + "physical_size": { + "type": "long" + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "virtual_address": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + } + }, + "segments": { + "type": "nested", + "properties": { + "sections": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "shared_libraries": { + "type": "keyword", + "ignore_above": 1024 + }, + "telfhash": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "extension": { + "type": "keyword", + "ignore_above": 1024 + }, + "gid": { + "type": "keyword", + "ignore_above": 1024 + }, + "group": { + "type": "keyword", + "ignore_above": 1024 + }, + "hash": { + "properties": { + "md5": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha1": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha256": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha512": { + "type": "keyword", + "ignore_above": 1024 + }, + "ssdeep": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "inode": { + "type": "keyword", + "ignore_above": 1024 + }, + "mime_type": { + "type": "keyword", + "ignore_above": 1024 + }, + "mode": { + "type": "keyword", + "ignore_above": 1024 + }, + "mtime": { + "type": "date" + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "owner": { + "type": "keyword", + "ignore_above": 1024 + }, + "path": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "pe": { + "properties": { + "architecture": { + "type": "keyword", + "ignore_above": 1024 + }, + "company": { + "type": "keyword", + "ignore_above": 1024 + }, + "description": { + "type": "keyword", + "ignore_above": 1024 + }, + "file_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "imphash": { + "type": "keyword", + "ignore_above": 1024 + }, + "original_file_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "product": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "size": { + "type": "long" + }, + "target_path": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "uid": { + "type": "keyword", + "ignore_above": 1024 + }, + "x509": { + "properties": { + "alternative_names": { + "type": "keyword", + "ignore_above": 1024 + }, + "issuer": { + "properties": { + "common_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country": { + "type": "keyword", + "ignore_above": 1024 + }, + "distinguished_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "locality": { + "type": "keyword", + "ignore_above": 1024 + }, + "organization": { + "type": "keyword", + "ignore_above": 1024 + }, + "organizational_unit": { + "type": "keyword", + "ignore_above": 1024 + }, + "state_or_province": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "type": "keyword", + "ignore_above": 1024 + }, + "public_key_curve": { + "type": "keyword", + "ignore_above": 1024 + }, + "public_key_exponent": { + "type": "long", + "index": false, + "doc_values": false + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "type": "keyword", + "ignore_above": 1024 + }, + "signature_algorithm": { + "type": "keyword", + "ignore_above": 1024 + }, + "subject": { + "properties": { + "common_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country": { + "type": "keyword", + "ignore_above": 1024 + }, + "distinguished_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "locality": { + "type": "keyword", + "ignore_above": 1024 + }, + "organization": { + "type": "keyword", + "ignore_above": 1024 + }, + "organizational_unit": { + "type": "keyword", + "ignore_above": 1024 + }, + "state_or_province": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "version_number": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "geo": { + "properties": { + "city_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "continent_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "location": { + "type": "geo_point" + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "region_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "region_name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "group": { + "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "hash": { + "properties": { + "md5": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha1": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha256": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha512": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "host": { + "properties": { + "architecture": { + "type": "keyword", + "ignore_above": 1024 + }, + "cpu": { + "properties": { + "usage": { + "type": "scaled_float", + "scaling_factor": 1000 + } + } + }, + "disk": { + "properties": { + "read": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "write": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "geo": { + "properties": { + "city_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "continent_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "continent_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "location": { + "type": "geo_point" + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "postal_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "region_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "region_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "timezone": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "hostname": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "ip": { + "type": "ip" + }, + "mac": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "network": { + "properties": { + "egress": { + "properties": { + "bytes": { + "type": "long" + }, + "packets": { + "type": "long" + } + } + }, + "ingress": { + "properties": { + "bytes": { + "type": "long" + }, + "packets": { + "type": "long" + } + } + } + } + }, + "os": { + "properties": { + "family": { + "type": "keyword", + "ignore_above": 1024 + }, + "full": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "kernel": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "caseless": { + "type": "keyword", + "ignore_above": 1024, + "normalizer": "lowercase" + }, + "text": { + "type": "text", + "norms": false + } + } + }, + "platform": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "uptime": { + "type": "long" + }, + "user": { + "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "email": { + "type": "keyword", + "ignore_above": 1024 + }, + "full_name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "group": { + "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "hash": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "roles": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "http": { + "properties": { + "request": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "method": { + "type": "keyword", + "ignore_above": 1024 + }, + "mime_type": { + "type": "keyword", + "ignore_above": 1024 + }, + "referrer": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "response": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "mime_type": { + "type": "keyword", + "ignore_above": 1024 + }, + "status_code": { + "type": "long" + } + } + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "interface": { + "properties": { + "alias": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "kibana": { + "properties": { + "alert": { + "properties": { + "ancestors": { + "properties": { + "depth": { + "type": "alias", + "path": "signal.ancestors.depth" + }, + "id": { + "type": "alias", + "path": "signal.ancestors.id" + }, + "index": { + "type": "alias", + "path": "signal.ancestors.index" + }, + "type": { + "type": "alias", + "path": "signal.ancestors.type" + } + } + }, + "depth": { + "type": "alias", + "path": "signal.depth" + }, + "original_event": { + "properties": { + "action": { + "type": "alias", + "path": "signal.original_event.action" + }, + "category": { + "type": "alias", + "path": "signal.original_event.category" + }, + "code": { + "type": "alias", + "path": "signal.original_event.code" + }, + "created": { + "type": "alias", + "path": "signal.original_event.created" + }, + "dataset": { + "type": "alias", + "path": "signal.original_event.dataset" + }, + "duration": { + "type": "alias", + "path": "signal.original_event.duration" + }, + "end": { + "type": "alias", + "path": "signal.original_event.end" + }, + "hash": { + "type": "alias", + "path": "signal.original_event.hash" + }, + "id": { + "type": "alias", + "path": "signal.original_event.id" + }, + "kind": { + "type": "alias", + "path": "signal.original_event.kind" + }, + "module": { + "type": "alias", + "path": "signal.original_event.module" + }, + "outcome": { + "type": "alias", + "path": "signal.original_event.outcome" + }, + "provider": { + "type": "alias", + "path": "signal.original_event.provider" + }, + "reason": { + "type": "alias", + "path": "signal.original_event.reason" + }, + "risk_score": { + "type": "alias", + "path": "signal.original_event.risk_score" + }, + "risk_score_norm": { + "type": "alias", + "path": "signal.original_event.risk_score_norm" + }, + "sequence": { + "type": "alias", + "path": "signal.original_event.sequence" + }, + "severity": { + "type": "alias", + "path": "signal.original_event.severity" + }, + "start": { + "type": "alias", + "path": "signal.original_event.start" + }, + "timezone": { + "type": "alias", + "path": "signal.original_event.timezone" + }, + "type": { + "type": "alias", + "path": "signal.original_event.type" + } + } + }, + "original_time": { + "type": "alias", + "path": "signal.original_time" + }, + "reason": { + "type": "alias", + "path": "signal.reason" + }, + "risk_score": { + "type": "alias", + "path": "signal.rule.risk_score" + }, + "rule": { + "properties": { + "author": { + "type": "alias", + "path": "signal.rule.author" + }, + "building_block_type": { + "type": "alias", + "path": "signal.rule.building_block_type" + }, + "created_at": { + "type": "alias", + "path": "signal.rule.created_at" + }, + "created_by": { + "type": "alias", + "path": "signal.rule.created_by" + }, + "description": { + "type": "alias", + "path": "signal.rule.description" + }, + "enabled": { + "type": "alias", + "path": "signal.rule.enabled" + }, + "false_positives": { + "type": "alias", + "path": "signal.rule.false_positives" + }, + "from": { + "type": "alias", + "path": "signal.rule.from" + }, + "immutable": { + "type": "alias", + "path": "signal.rule.immutable" + }, + "index": { + "type": "alias", + "path": "signal.rule.index" + }, + "interval": { + "type": "alias", + "path": "signal.rule.interval" + }, + "language": { + "type": "alias", + "path": "signal.rule.language" + }, + "license": { + "type": "alias", + "path": "signal.rule.license" + }, + "max_signals": { + "type": "alias", + "path": "signal.rule.max_signals" + }, + "name": { + "type": "alias", + "path": "signal.rule.name" + }, + "note": { + "type": "alias", + "path": "signal.rule.note" + }, + "query": { + "type": "alias", + "path": "signal.rule.query" + }, + "references": { + "type": "alias", + "path": "signal.rule.references" + }, + "risk_score_mapping": { + "properties": { + "field": { + "type": "alias", + "path": "signal.rule.risk_score_mapping.field" + }, + "operator": { + "type": "alias", + "path": "signal.rule.risk_score_mapping.operator" + }, + "value": { + "type": "alias", + "path": "signal.rule.risk_score_mapping.value" + } + } + }, + "rule_id": { + "type": "alias", + "path": "signal.rule.rule_id" + }, + "rule_name_override": { + "type": "alias", + "path": "signal.rule.rule_name_override" + }, + "saved_id": { + "type": "alias", + "path": "signal.rule.saved_id" + }, + "severity_mapping": { + "properties": { + "field": { + "type": "alias", + "path": "signal.rule.severity_mapping.field" + }, + "operator": { + "type": "alias", + "path": "signal.rule.severity_mapping.operator" + }, + "severity": { + "type": "alias", + "path": "signal.rule.severity_mapping.severity" + }, + "value": { + "type": "alias", + "path": "signal.rule.severity_mapping.value" + } + } + }, + "tags": { + "type": "alias", + "path": "signal.rule.tags" + }, + "threat": { + "properties": { + "framework": { + "type": "alias", + "path": "signal.rule.threat.framework" + }, + "tactic": { + "properties": { + "id": { + "type": "alias", + "path": "signal.rule.threat.tactic.id" + }, + "name": { + "type": "alias", + "path": "signal.rule.threat.tactic.name" + }, + "reference": { + "type": "alias", + "path": "signal.rule.threat.tactic.reference" + } + } + }, + "technique": { + "properties": { + "id": { + "type": "alias", + "path": "signal.rule.threat.technique.id" + }, + "name": { + "type": "alias", + "path": "signal.rule.threat.technique.name" + }, + "reference": { + "type": "alias", + "path": "signal.rule.threat.technique.reference" + }, + "subtechnique": { + "properties": { + "id": { + "type": "alias", + "path": "signal.rule.threat.technique.subtechnique.id" + }, + "name": { + "type": "alias", + "path": "signal.rule.threat.technique.subtechnique.name" + }, + "reference": { + "type": "alias", + "path": "signal.rule.threat.technique.subtechnique.reference" + } + } + } + } + } + } + }, + "threat_index": { + "type": "alias", + "path": "signal.rule.threat_index" + }, + "threat_indicator_path": { + "type": "alias", + "path": "signal.rule.threat_indicator_path" + }, + "threat_language": { + "type": "alias", + "path": "signal.rule.threat_language" + }, + "threat_mapping": { + "properties": { + "entries": { + "properties": { + "field": { + "type": "alias", + "path": "signal.rule.threat_mapping.entries.field" + }, + "type": { + "type": "alias", + "path": "signal.rule.threat_mapping.entries.type" + }, + "value": { + "type": "alias", + "path": "signal.rule.threat_mapping.entries.value" + } + } + } + } + }, + "threat_query": { + "type": "alias", + "path": "signal.rule.threat_query" + }, + "threshold": { + "properties": { + "field": { + "type": "alias", + "path": "signal.rule.threshold.field" + }, + "value": { + "type": "alias", + "path": "signal.rule.threshold.value" + } + } + }, + "timeline_id": { + "type": "alias", + "path": "signal.rule.timeline_id" + }, + "timeline_title": { + "type": "alias", + "path": "signal.rule.timeline_title" + }, + "to": { + "type": "alias", + "path": "signal.rule.to" + }, + "type": { + "type": "alias", + "path": "signal.rule.type" + }, + "updated_at": { + "type": "alias", + "path": "signal.rule.updated_at" + }, + "updated_by": { + "type": "alias", + "path": "signal.rule.updated_by" + }, + "uuid": { + "type": "alias", + "path": "signal.rule.id" + }, + "version": { + "type": "alias", + "path": "signal.rule.version" + } + } + }, + "severity": { + "type": "alias", + "path": "signal.rule.severity" + }, + "threshold_result": { + "properties": { + "cardinality": { + "properties": { + "field": { + "type": "alias", + "path": "signal.threshold_result.cardinality.field" + }, + "value": { + "type": "alias", + "path": "signal.threshold_result.cardinality.value" + } + } + }, + "count": { + "type": "alias", + "path": "signal.threshold_result.count" + }, + "from": { + "type": "alias", + "path": "signal.threshold_result.from" + }, + "terms": { + "properties": { + "field": { + "type": "alias", + "path": "signal.threshold_result.terms.field" + }, + "value": { + "type": "alias", + "path": "signal.threshold_result.terms.value" + } + } + } + } + }, + "workflow_status": { + "type": "alias", + "path": "signal.status" + } + } + } + } + }, + "labels": { + "type": "object" + }, + "log": { + "properties": { + "file": { + "properties": { + "path": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "level": { + "type": "keyword", + "ignore_above": 1024 + }, + "logger": { + "type": "keyword", + "ignore_above": 1024 + }, + "origin": { + "properties": { + "file": { + "properties": { + "line": { + "type": "integer" + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "function": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "original": { + "type": "keyword", + "index": false, + "doc_values": false + }, + "syslog": { + "properties": { + "facility": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "priority": { + "type": "long" + }, + "severity": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + } + } + }, + "message": { + "type": "text", + "norms": false + }, + "network": { + "properties": { + "application": { + "type": "keyword", + "ignore_above": 1024 + }, + "bytes": { + "type": "long" + }, + "community_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "direction": { + "type": "keyword", + "ignore_above": 1024 + }, + "forwarded_ip": { + "type": "ip" + }, + "iana_number": { + "type": "keyword", + "ignore_above": 1024 + }, + "inner": { + "properties": { + "vlan": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "packets": { + "type": "long" + }, + "protocol": { + "type": "keyword", + "ignore_above": 1024 + }, + "transport": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "vlan": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "observer": { + "properties": { + "egress": { + "properties": { + "interface": { + "properties": { + "alias": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "vlan": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "zone": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "geo": { + "properties": { + "city_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "continent_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "continent_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "location": { + "type": "geo_point" + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "postal_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "region_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "region_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "timezone": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "hostname": { + "type": "keyword", + "ignore_above": 1024 + }, + "ingress": { + "properties": { + "interface": { + "properties": { + "alias": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "vlan": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "zone": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "os": { + "properties": { + "family": { + "type": "keyword", + "ignore_above": 1024 + }, + "full": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "kernel": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "platform": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "product": { + "type": "keyword", + "ignore_above": 1024 + }, + "serial_number": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "vendor": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "orchestrator": { + "properties": { + "api_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "cluster": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "url": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "namespace": { + "type": "keyword", + "ignore_above": 1024 + }, + "organization": { + "type": "keyword", + "ignore_above": 1024 + }, + "resource": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "organization": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + } + } + }, + "os": { + "properties": { + "family": { + "type": "keyword", + "ignore_above": 1024 + }, + "full": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "kernel": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "platform": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "package": { + "properties": { + "architecture": { + "type": "keyword", + "ignore_above": 1024 + }, + "build_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "checksum": { + "type": "keyword", + "ignore_above": 1024 + }, + "description": { + "type": "keyword", + "ignore_above": 1024 + }, + "install_scope": { + "type": "keyword", + "ignore_above": 1024 + }, + "installed": { + "type": "date" + }, + "license": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "path": { + "type": "keyword", + "ignore_above": 1024 + }, + "reference": { + "type": "keyword", + "ignore_above": 1024 + }, + "size": { + "type": "long" + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "pe": { + "properties": { + "company": { + "type": "keyword", + "ignore_above": 1024 + }, + "description": { + "type": "keyword", + "ignore_above": 1024 + }, + "file_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "original_file_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "product": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "process": { + "properties": { + "args": { + "type": "keyword", + "ignore_above": 1024 + }, + "args_count": { + "type": "long" + }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "signing_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "status": { + "type": "keyword", + "ignore_above": 1024 + }, + "subject_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "team_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "command_line": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "elf": { + "properties": { + "architecture": { + "type": "keyword", + "ignore_above": 1024 + }, + "byte_order": { + "type": "keyword", + "ignore_above": 1024 + }, + "cpu_type": { + "type": "keyword", + "ignore_above": 1024 + }, + "creation_date": { + "type": "date" + }, + "exports": { + "type": "flattened" + }, + "header": { + "properties": { + "abi_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "class": { + "type": "keyword", + "ignore_above": 1024 + }, + "data": { + "type": "keyword", + "ignore_above": 1024 + }, + "entrypoint": { + "type": "long" + }, + "object_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "os_abi": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "imports": { + "type": "flattened" + }, + "sections": { + "type": "nested", + "properties": { + "chi2": { + "type": "long" + }, + "entropy": { + "type": "long" + }, + "flags": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "physical_offset": { + "type": "keyword", + "ignore_above": 1024 + }, + "physical_size": { + "type": "long" + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "virtual_address": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + } + }, + "segments": { + "type": "nested", + "properties": { + "sections": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "shared_libraries": { + "type": "keyword", + "ignore_above": 1024 + }, + "telfhash": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "entity_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "executable": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "exit_code": { + "type": "long" + }, + "hash": { + "properties": { + "md5": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha1": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha256": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha512": { + "type": "keyword", + "ignore_above": 1024 + }, + "ssdeep": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "parent": { + "properties": { + "args": { + "type": "keyword", + "ignore_above": 1024 + }, + "args_count": { + "type": "long" + }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "signing_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "status": { + "type": "keyword", + "ignore_above": 1024 + }, + "subject_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "team_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "command_line": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "elf": { + "properties": { + "architecture": { + "type": "keyword", + "ignore_above": 1024 + }, + "byte_order": { + "type": "keyword", + "ignore_above": 1024 + }, + "cpu_type": { + "type": "keyword", + "ignore_above": 1024 + }, + "creation_date": { + "type": "date" + }, + "exports": { + "type": "flattened" + }, + "header": { + "properties": { + "abi_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "class": { + "type": "keyword", + "ignore_above": 1024 + }, + "data": { + "type": "keyword", + "ignore_above": 1024 + }, + "entrypoint": { + "type": "long" + }, + "object_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "os_abi": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "imports": { + "type": "flattened" + }, + "sections": { + "type": "nested", + "properties": { + "chi2": { + "type": "long" + }, + "entropy": { + "type": "long" + }, + "flags": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "physical_offset": { + "type": "keyword", + "ignore_above": 1024 + }, + "physical_size": { + "type": "long" + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "virtual_address": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + } + }, + "segments": { + "type": "nested", + "properties": { + "sections": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "shared_libraries": { + "type": "keyword", + "ignore_above": 1024 + }, + "telfhash": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "entity_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "executable": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "exit_code": { + "type": "long" + }, + "hash": { + "properties": { + "md5": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha1": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha256": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha512": { + "type": "keyword", + "ignore_above": 1024 + }, + "ssdeep": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "pe": { + "properties": { + "architecture": { + "type": "keyword", + "ignore_above": 1024 + }, + "company": { + "type": "keyword", + "ignore_above": 1024 + }, + "description": { + "type": "keyword", + "ignore_above": 1024 + }, + "file_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "imphash": { + "type": "keyword", + "ignore_above": 1024 + }, + "original_file_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "product": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "title": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + } + } + }, + "pe": { + "properties": { + "architecture": { + "type": "keyword", + "ignore_above": 1024 + }, + "company": { + "type": "keyword", + "ignore_above": 1024 + }, + "description": { + "type": "keyword", + "ignore_above": 1024 + }, + "file_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "imphash": { + "type": "keyword", + "ignore_above": 1024 + }, + "original_file_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "product": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "title": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + } + } + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "type": "keyword", + "ignore_above": 1024 + }, + "strings": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "hive": { + "type": "keyword", + "ignore_above": 1024 + }, + "key": { + "type": "keyword", + "ignore_above": 1024 + }, + "path": { + "type": "keyword", + "ignore_above": 1024 + }, + "value": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "related": { + "properties": { + "hash": { + "type": "keyword", + "ignore_above": 1024 + }, + "hosts": { + "type": "keyword", + "ignore_above": 1024 + }, + "ip": { + "type": "ip" + }, + "user": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "rule": { + "properties": { + "author": { + "type": "keyword", + "ignore_above": 1024 + }, + "category": { + "type": "keyword", + "ignore_above": 1024 + }, + "description": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "license": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "reference": { + "type": "keyword", + "ignore_above": 1024 + }, + "ruleset": { + "type": "keyword", + "ignore_above": 1024 + }, + "uuid": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "server": { + "properties": { + "address": { + "type": "keyword", + "ignore_above": 1024 + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "geo": { + "properties": { + "city_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "continent_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "continent_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "location": { + "type": "geo_point" + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "postal_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "region_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "region_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "timezone": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "type": "keyword", + "ignore_above": 1024 + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "subdomain": { + "type": "keyword", + "ignore_above": 1024 + }, + "top_level_domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "user": { + "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "email": { + "type": "keyword", + "ignore_above": 1024 + }, + "full_name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "group": { + "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "hash": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "roles": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "service": { + "properties": { + "ephemeral_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "node": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "state": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "signal": { + "properties": { + "_meta": { + "properties": { + "version": { + "type": "long" + } + } + }, + "ancestors": { + "properties": { + "depth": { + "type": "long" + }, + "id": { + "type": "keyword" + }, + "index": { + "type": "keyword" + }, + "rule": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "depth": { + "type": "integer" + }, + "group": { + "properties": { + "id": { + "type": "keyword" + }, + "index": { + "type": "integer" + } + } + }, + "original_event": { + "properties": { + "action": { + "type": "keyword" + }, + "category": { + "type": "keyword" + }, + "code": { + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "type": "keyword" + }, + "duration": { + "type": "long" + }, + "end": { + "type": "date" + }, + "hash": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "kind": { + "type": "keyword" + }, + "module": { + "type": "keyword" + }, + "original": { + "type": "keyword", + "index": false, + "doc_values": false + }, + "outcome": { + "type": "keyword" + }, + "provider": { + "type": "keyword" + }, + "reason": { + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "original_signal": { + "type": "object", + "dynamic": "false", + "enabled": false + }, + "original_time": { + "type": "date" + }, + "parent": { + "properties": { + "depth": { + "type": "long" + }, + "id": { + "type": "keyword" + }, + "index": { + "type": "keyword" + }, + "rule": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "parents": { + "properties": { + "depth": { + "type": "long" + }, + "id": { + "type": "keyword" + }, + "index": { + "type": "keyword" + }, + "rule": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "reason": { + "type": "keyword" + }, + "rule": { + "properties": { + "author": { + "type": "keyword" + }, + "building_block_type": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "keyword" + }, + "enabled": { + "type": "keyword" + }, + "false_positives": { + "type": "keyword" + }, + "filters": { + "type": "object" + }, + "from": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "immutable": { + "type": "keyword" + }, + "index": { + "type": "keyword" + }, + "interval": { + "type": "keyword" + }, + "language": { + "type": "keyword" + }, + "license": { + "type": "keyword" + }, + "max_signals": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "note": { + "type": "text" + }, + "output_index": { + "type": "keyword" + }, + "query": { + "type": "keyword" + }, + "references": { + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_mapping": { + "properties": { + "field": { + "type": "keyword" + }, + "operator": { + "type": "keyword" + }, + "value": { + "type": "keyword" + } + } + }, + "rule_id": { + "type": "keyword" + }, + "rule_name_override": { + "type": "keyword" + }, + "saved_id": { + "type": "keyword" + }, + "severity": { + "type": "keyword" + }, + "severity_mapping": { + "properties": { + "field": { + "type": "keyword" + }, + "operator": { + "type": "keyword" + }, + "severity": { + "type": "keyword" + }, + "value": { + "type": "keyword" + } + } + }, + "size": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "threat": { + "properties": { + "framework": { + "type": "keyword" + }, + "tactic": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "reference": { + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "reference": { + "type": "keyword" + }, + "subtechnique": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "reference": { + "type": "keyword" + } + } + } + } + } + } + }, + "threat_filters": { + "type": "object" + }, + "threat_index": { + "type": "keyword" + }, + "threat_indicator_path": { + "type": "keyword" + }, + "threat_language": { + "type": "keyword" + }, + "threat_mapping": { + "properties": { + "entries": { + "properties": { + "field": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "type": "keyword" + } + } + } + } + }, + "threat_query": { + "type": "keyword" + }, + "threshold": { + "properties": { + "field": { + "type": "keyword" + }, + "value": { + "type": "float" + } + } + }, + "timeline_id": { + "type": "keyword" + }, + "timeline_title": { + "type": "keyword" + }, + "timestamp_override": { + "type": "keyword" + }, + "to": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "status": { + "type": "keyword" + }, + "threshold_count": { + "type": "float" + }, + "threshold_result": { + "properties": { + "cardinality": { + "properties": { + "field": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + }, + "count": { + "type": "long" + }, + "from": { + "type": "date" + }, + "terms": { + "properties": { + "field": { + "type": "keyword" + }, + "value": { + "type": "keyword" + } + } + } + } + } + } + }, + "source": { + "properties": { + "address": { + "type": "keyword", + "ignore_above": 1024 + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "geo": { + "properties": { + "city_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "continent_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "continent_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "location": { + "type": "geo_point" + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "postal_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "region_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "region_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "timezone": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "type": "keyword", + "ignore_above": 1024 + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "subdomain": { + "type": "keyword", + "ignore_above": 1024 + }, + "top_level_domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "user": { + "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "email": { + "type": "keyword", + "ignore_above": 1024 + }, + "full_name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "group": { + "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "hash": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "roles": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "span": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "tags": { + "type": "keyword", + "ignore_above": 1024 + }, + "threat": { + "properties": { + "enrichments": { + "type": "nested", + "properties": { + "indicator": { + "properties": { + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + } + } + } + } + }, + "confidence": { + "type": "keyword", + "ignore_above": 1024 + }, + "description": { + "type": "keyword", + "ignore_above": 1024 + }, + "email": { + "properties": { + "address": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "type": "keyword", + "ignore_above": 1024 + }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "signing_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "status": { + "type": "keyword", + "ignore_above": 1024 + }, + "subject_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "team_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "type": "keyword", + "ignore_above": 1024 + }, + "directory": { + "type": "keyword", + "ignore_above": 1024 + }, + "drive_letter": { + "type": "keyword", + "ignore_above": 1 + }, + "elf": { + "properties": { + "architecture": { + "type": "keyword", + "ignore_above": 1024 + }, + "byte_order": { + "type": "keyword", + "ignore_above": 1024 + }, + "cpu_type": { + "type": "keyword", + "ignore_above": 1024 + }, + "creation_date": { + "type": "date" + }, + "exports": { + "type": "flattened" + }, + "header": { + "properties": { + "abi_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "class": { + "type": "keyword", + "ignore_above": 1024 + }, + "data": { + "type": "keyword", + "ignore_above": 1024 + }, + "entrypoint": { + "type": "long" + }, + "object_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "os_abi": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "imports": { + "type": "flattened" + }, + "sections": { + "type": "nested", + "properties": { + "chi2": { + "type": "long" + }, + "entropy": { + "type": "long" + }, + "flags": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "physical_offset": { + "type": "keyword", + "ignore_above": 1024 + }, + "physical_size": { + "type": "long" + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "virtual_address": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + } + }, + "segments": { + "type": "nested", + "properties": { + "sections": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "shared_libraries": { + "type": "keyword", + "ignore_above": 1024 + }, + "telfhash": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "extension": { + "type": "keyword", + "ignore_above": 1024 + }, + "gid": { + "type": "keyword", + "ignore_above": 1024 + }, + "group": { + "type": "keyword", + "ignore_above": 1024 + }, + "inode": { + "type": "keyword", + "ignore_above": 1024 + }, + "mime_type": { + "type": "keyword", + "ignore_above": 1024 + }, + "mode": { + "type": "keyword", + "ignore_above": 1024 + }, + "mtime": { + "type": "date" + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "owner": { + "type": "keyword", + "ignore_above": 1024 + }, + "path": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "size": { + "type": "long" + }, + "target_path": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "uid": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "first_seen": { + "type": "date" + }, + "geo": { + "properties": { + "city_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "continent_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "continent_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "country_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "location": { + "type": "geo_point" + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "postal_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "region_iso_code": { + "type": "keyword", + "ignore_above": 1024 + }, + "region_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "timezone": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "hash": { + "properties": { + "md5": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha1": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha256": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha512": { + "type": "keyword", + "ignore_above": 1024 + }, + "ssdeep": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "ip": { + "type": "ip" + }, + "last_seen": { + "type": "date" + }, + "marking": { + "properties": { + "tlp": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "modified_at": { + "type": "date" + }, + "pe": { + "properties": { + "architecture": { + "type": "keyword", + "ignore_above": 1024 + }, + "company": { + "type": "keyword", + "ignore_above": 1024 + }, + "description": { + "type": "keyword", + "ignore_above": 1024 + }, + "file_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "imphash": { + "type": "keyword", + "ignore_above": 1024 + }, + "original_file_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "product": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "port": { + "type": "long" + }, + "provider": { + "type": "keyword", + "ignore_above": 1024 + }, + "reference": { + "type": "keyword", + "ignore_above": 1024 + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "type": "keyword", + "ignore_above": 1024 + }, + "strings": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "hive": { + "type": "keyword", + "ignore_above": 1024 + }, + "key": { + "type": "keyword", + "ignore_above": 1024 + }, + "path": { + "type": "keyword", + "ignore_above": 1024 + }, + "value": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "scanner_stats": { + "type": "long" + }, + "sightings": { + "type": "long" + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "url": { + "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "extension": { + "type": "keyword", + "ignore_above": 1024 + }, + "fragment": { + "type": "keyword", + "ignore_above": 1024 + }, + "full": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "original": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "password": { + "type": "keyword", + "ignore_above": 1024 + }, + "path": { + "type": "keyword", + "ignore_above": 1024 + }, + "port": { + "type": "long" + }, + "query": { + "type": "keyword", + "ignore_above": 1024 + }, + "registered_domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "scheme": { + "type": "keyword", + "ignore_above": 1024 + }, + "subdomain": { + "type": "keyword", + "ignore_above": 1024 + }, + "top_level_domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "username": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "x509": { + "properties": { + "alternative_names": { + "type": "keyword", + "ignore_above": 1024 + }, + "issuer": { + "properties": { + "common_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country": { + "type": "keyword", + "ignore_above": 1024 + }, + "distinguished_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "locality": { + "type": "keyword", + "ignore_above": 1024 + }, + "organization": { + "type": "keyword", + "ignore_above": 1024 + }, + "organizational_unit": { + "type": "keyword", + "ignore_above": 1024 + }, + "state_or_province": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "type": "keyword", + "ignore_above": 1024 + }, + "public_key_curve": { + "type": "keyword", + "ignore_above": 1024 + }, + "public_key_exponent": { + "type": "long", + "index": false, + "doc_values": false + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "type": "keyword", + "ignore_above": 1024 + }, + "signature_algorithm": { + "type": "keyword", + "ignore_above": 1024 + }, + "subject": { + "properties": { + "common_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country": { + "type": "keyword", + "ignore_above": 1024 + }, + "distinguished_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "locality": { + "type": "keyword", + "ignore_above": 1024 + }, + "organization": { + "type": "keyword", + "ignore_above": 1024 + }, + "organizational_unit": { + "type": "keyword", + "ignore_above": 1024 + }, + "state_or_province": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "version_number": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "matched": { + "properties": { + "atomic": { + "type": "keyword", + "ignore_above": 1024 + }, + "field": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "index": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "framework": { + "type": "keyword", + "ignore_above": 1024 + }, + "group": { + "properties": { + "alias": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "reference": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "software": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "platforms": { + "type": "keyword", + "ignore_above": 1024 + }, + "reference": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "tactic": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "reference": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "technique": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "reference": { + "type": "keyword", + "ignore_above": 1024 + }, + "subtechnique": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "reference": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + } + } + }, + "tls": { + "properties": { + "cipher": { + "type": "keyword", + "ignore_above": 1024 + }, + "client": { + "properties": { + "certificate": { + "type": "keyword", + "ignore_above": 1024 + }, + "certificate_chain": { + "type": "keyword", + "ignore_above": 1024 + }, + "hash": { + "properties": { + "md5": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha1": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha256": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "issuer": { + "type": "keyword", + "ignore_above": 1024 + }, + "ja3": { + "type": "keyword", + "ignore_above": 1024 + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "server_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "subject": { + "type": "keyword", + "ignore_above": 1024 + }, + "supported_ciphers": { + "type": "keyword", + "ignore_above": 1024 + }, + "x509": { + "properties": { + "alternative_names": { + "type": "keyword", + "ignore_above": 1024 + }, + "issuer": { + "properties": { + "common_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country": { + "type": "keyword", + "ignore_above": 1024 + }, + "distinguished_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "locality": { + "type": "keyword", + "ignore_above": 1024 + }, + "organization": { + "type": "keyword", + "ignore_above": 1024 + }, + "organizational_unit": { + "type": "keyword", + "ignore_above": 1024 + }, + "state_or_province": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "type": "keyword", + "ignore_above": 1024 + }, + "public_key_curve": { + "type": "keyword", + "ignore_above": 1024 + }, + "public_key_exponent": { + "type": "long", + "index": false, + "doc_values": false + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "type": "keyword", + "ignore_above": 1024 + }, + "signature_algorithm": { + "type": "keyword", + "ignore_above": 1024 + }, + "subject": { + "properties": { + "common_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country": { + "type": "keyword", + "ignore_above": 1024 + }, + "distinguished_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "locality": { + "type": "keyword", + "ignore_above": 1024 + }, + "organization": { + "type": "keyword", + "ignore_above": 1024 + }, + "organizational_unit": { + "type": "keyword", + "ignore_above": 1024 + }, + "state_or_province": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "version_number": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "curve": { + "type": "keyword", + "ignore_above": 1024 + }, + "established": { + "type": "boolean" + }, + "next_protocol": { + "type": "keyword", + "ignore_above": 1024 + }, + "resumed": { + "type": "boolean" + }, + "server": { + "properties": { + "certificate": { + "type": "keyword", + "ignore_above": 1024 + }, + "certificate_chain": { + "type": "keyword", + "ignore_above": 1024 + }, + "hash": { + "properties": { + "md5": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha1": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha256": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "issuer": { + "type": "keyword", + "ignore_above": 1024 + }, + "ja3s": { + "type": "keyword", + "ignore_above": 1024 + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "subject": { + "type": "keyword", + "ignore_above": 1024 + }, + "x509": { + "properties": { + "alternative_names": { + "type": "keyword", + "ignore_above": 1024 + }, + "issuer": { + "properties": { + "common_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country": { + "type": "keyword", + "ignore_above": 1024 + }, + "distinguished_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "locality": { + "type": "keyword", + "ignore_above": 1024 + }, + "organization": { + "type": "keyword", + "ignore_above": 1024 + }, + "organizational_unit": { + "type": "keyword", + "ignore_above": 1024 + }, + "state_or_province": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "type": "keyword", + "ignore_above": 1024 + }, + "public_key_curve": { + "type": "keyword", + "ignore_above": 1024 + }, + "public_key_exponent": { + "type": "long", + "index": false, + "doc_values": false + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "type": "keyword", + "ignore_above": 1024 + }, + "signature_algorithm": { + "type": "keyword", + "ignore_above": 1024 + }, + "subject": { + "properties": { + "common_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "country": { + "type": "keyword", + "ignore_above": 1024 + }, + "distinguished_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "locality": { + "type": "keyword", + "ignore_above": 1024 + }, + "organization": { + "type": "keyword", + "ignore_above": 1024 + }, + "organizational_unit": { + "type": "keyword", + "ignore_above": 1024 + }, + "state_or_province": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "version_number": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + }, + "version_protocol": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "trace": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "transaction": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "url": { + "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "extension": { + "type": "keyword", + "ignore_above": 1024 + }, + "fragment": { + "type": "keyword", + "ignore_above": 1024 + }, + "full": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "original": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "password": { + "type": "keyword", + "ignore_above": 1024 + }, + "path": { + "type": "keyword", + "ignore_above": 1024 + }, + "port": { + "type": "long" + }, + "query": { + "type": "keyword", + "ignore_above": 1024 + }, + "registered_domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "scheme": { + "type": "keyword", + "ignore_above": 1024 + }, + "subdomain": { + "type": "keyword", + "ignore_above": 1024 + }, + "top_level_domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "username": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "user": { + "properties": { + "changes": { + "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "email": { + "type": "keyword", + "ignore_above": 1024 + }, + "full_name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "group": { + "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "hash": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "roles": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "effective": { + "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "email": { + "type": "keyword", + "ignore_above": 1024 + }, + "full_name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "group": { + "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "hash": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "roles": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "email": { + "type": "keyword", + "ignore_above": 1024 + }, + "full_name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "group": { + "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "hash": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "roles": { + "type": "keyword", + "ignore_above": 1024 + }, + "target": { + "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "email": { + "type": "keyword", + "ignore_above": 1024 + }, + "full_name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "group": { + "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "hash": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "roles": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "user_agent": { + "properties": { + "device": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "original": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "os": { + "properties": { + "family": { + "type": "keyword", + "ignore_above": 1024 + }, + "full": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "kernel": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "platform": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "vlan": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "vulnerability": { + "properties": { + "category": { + "type": "keyword", + "ignore_above": 1024 + }, + "classification": { + "type": "keyword", + "ignore_above": 1024 + }, + "description": { + "type": "keyword", + "ignore_above": 1024, + "fields": { + "text": { + "type": "text", + "norms": false + } + } + }, + "enumeration": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "reference": { + "type": "keyword", + "ignore_above": 1024 + }, + "report_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "scanner": { + "properties": { + "vendor": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "score": { + "properties": { + "base": { + "type": "float" + }, + "environmental": { + "type": "float" + }, + "temporal": { + "type": "float" + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "severity": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "settings": { + "index": { + "lifecycle": { + "name": ".siem-signals-default", + "rollover_alias": ".siem-signals-default" + }, + "routing": { + "allocation": { + "include": { + "_tier_preference": "data_content" + } + } + }, + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "number_of_shards": "1", + "number_of_replicas": "1" + } + } + } +} \ No newline at end of file From 50f99676bf27ccae5d6ac97cab7b573330554a8c Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Tue, 29 Mar 2022 10:50:00 -0400 Subject: [PATCH 25/37] [Response Ops] Alerts table (#125977) * WIP * Fix some issues * WIP * Revert "WIP" This reverts commit 5583997921dca184227b9e1c84b1030bad2fbda1. * WIP * Mock data * WIP * WIP * Remove unnecessary logic * Add some basic tests * Slight rename * Fix unnecessary export * Fix types * Add ability to use externally * Add test data * Fix types and tests * LAZZYYYYYYY load * Improvements based on o11y integrations * Fix types and tests * Cleanup and unit tests * Better integration with o11y alerts * Use experimental flag * Add functional tests * Fix tests * Add unit test for home page, and stop using internal because we have the feature flag * Fix bad label * Update strategy name * Fix test * Fix test, part 2 Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/rule_registry/common/index.ts | 6 +- .../common/experimental_features.ts | 1 + .../public/application/app.tsx | 2 +- .../public/application/constants/index.ts | 3 +- .../public/application/home.test.tsx | 43 ++++ .../public/application/home.tsx | 29 ++- .../alerts_table/alerts_page/README.md | 1 + .../alerts_table/alerts_page/alerts_page.tsx | 195 ++++++++++++++++++ .../alerts_table/alerts_page/index.ts | 9 + .../alerts_table/alerts_table.test.tsx | 106 ++++++++++ .../sections/alerts_table/alerts_table.tsx | 46 +++++ .../sections/alerts_table/hooks/index.ts | 8 + .../alerts_table/hooks/use_pagination.test.ts | 45 ++++ .../alerts_table/hooks/use_pagination.ts | 38 ++++ .../alerts_table/hooks/use_sorting.test.ts | 33 +++ .../alerts_table/hooks/use_sorting.ts | 21 ++ .../sections/alerts_table/index.ts | 10 + .../sections/alerts_table/types.ts | 12 ++ .../public/common/get_alerts_table.tsx | 14 ++ .../common/get_experimental_features.test.tsx | 12 +- .../common/get_experimental_features.ts | 4 +- .../triggers_actions_ui/public/mocks.ts | 5 + .../triggers_actions_ui/public/plugin.ts | 6 + .../triggers_actions_ui/public/types.ts | 47 ++++- .../apps/triggers_actions_ui/alerts_table.ts | 157 ++++++++++++++ .../apps/triggers_actions_ui/index.ts | 1 + x-pack/test/functional_with_es_ssl/config.ts | 1 + 27 files changed, 846 insertions(+), 9 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_page/README.md create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_page/alerts_page.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_page/index.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/index.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_pagination.test.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_pagination.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_sorting.test.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_sorting.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/index.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/types.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/common/get_alerts_table.tsx create mode 100644 x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_table.ts diff --git a/x-pack/plugins/rule_registry/common/index.ts b/x-pack/plugins/rule_registry/common/index.ts index 2dd7f6bbc456e..1f6053735d9b7 100644 --- a/x-pack/plugins/rule_registry/common/index.ts +++ b/x-pack/plugins/rule_registry/common/index.ts @@ -5,5 +5,9 @@ * 2.0. */ export { parseTechnicalFields, type ParsedTechnicalFields } from './parse_technical_fields'; -export type { RuleRegistrySearchRequest, RuleRegistrySearchResponse } from './search_strategy'; +export type { + RuleRegistrySearchRequest, + RuleRegistrySearchResponse, + RuleRegistrySearchRequestPagination, +} from './search_strategy'; export { BASE_RAC_ALERTS_API_PATH } from './constants'; diff --git a/x-pack/plugins/triggers_actions_ui/common/experimental_features.ts b/x-pack/plugins/triggers_actions_ui/common/experimental_features.ts index c3ae062d76523..f95ce25122923 100644 --- a/x-pack/plugins/triggers_actions_ui/common/experimental_features.ts +++ b/x-pack/plugins/triggers_actions_ui/common/experimental_features.ts @@ -13,6 +13,7 @@ export type ExperimentalFeatures = typeof allowedExperimentalValues; */ export const allowedExperimentalValues = Object.freeze({ rulesListDatagrid: true, + internalAlertsTable: false, rulesDetailLogs: true, }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx index b2c350a4f1f29..0701eac20a850 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx @@ -59,7 +59,7 @@ export const renderApp = (deps: TriggersAndActionsUiServices) => { export const App = ({ deps }: { deps: TriggersAndActionsUiServices }) => { const { savedObjects, uiSettings, theme$ } = deps; - const sections: Section[] = ['rules', 'connectors']; + const sections: Section[] = ['rules', 'connectors', 'alerts']; const isDarkMode = useObservable(uiSettings.get$('theme:darkMode')); const sectionsRegex = sections.join('|'); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts index 0058147ca0d05..0d26abc3bc67b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts @@ -13,12 +13,13 @@ export { } from '../../../../alerting/common'; export { BASE_ACTION_API_PATH, INTERNAL_BASE_ACTION_API_PATH } from '../../../../actions/common'; -export type Section = 'connectors' | 'rules'; +export type Section = 'connectors' | 'rules' | 'alerts'; export const routeToHome = `/`; export const routeToConnectors = `/connectors`; export const routeToRules = `/rules`; export const routeToRuleDetails = `/rule/:ruleId`; +export const routeToInternalAlerts = `/alerts`; export const legacyRouteToRules = `/alerts`; export const legacyRouteToRuleDetails = `/alert/:alertId`; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/home.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/home.test.tsx index 51f80438c55cc..6236e9e2d3d27 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/home.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/home.test.tsx @@ -10,11 +10,20 @@ import { RouteComponentProps, Router } from 'react-router-dom'; import { createMemoryHistory, createLocation } from 'history'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import TriggersActionsUIHome, { MatchParams } from './home'; +import { hasShowActionsCapability } from './lib/capabilities'; import { useKibana } from '../common/lib/kibana'; +import { getIsExperimentalFeatureEnabled } from '../common/get_experimental_features'; jest.mock('../common/lib/kibana'); +jest.mock('../common/get_experimental_features'); +jest.mock('./lib/capabilities'); const useKibanaMock = useKibana as jest.Mocked; describe('home', () => { + beforeEach(() => { + (hasShowActionsCapability as jest.Mock).mockClear(); + (getIsExperimentalFeatureEnabled as jest.Mock).mockClear(); + }); + it('renders the documentation link', async () => { const props: RouteComponentProps = { history: createMemoryHistory(), @@ -40,4 +49,38 @@ describe('home', () => { 'https://www.elastic.co/guide/en/kibana/mocked-test-branch/create-and-manage-rules.html' ); }); + + it('hides the internal alerts table route if the config is not set', async () => { + (hasShowActionsCapability as jest.Mock).mockImplementation(() => { + return true; + }); + const props: RouteComponentProps = { + history: createMemoryHistory(), + location: createLocation('/'), + match: { + isExact: true, + path: `/connectorss`, + url: '', + params: { + section: 'connectors', + }, + }, + }; + + let home = mountWithIntl(); + + // Just rules/connectors + expect(home.find('.euiTab__content').length).toBe(2); + + (getIsExperimentalFeatureEnabled as jest.Mock).mockImplementation((feature: string) => { + if (feature === 'internalAlertsTable') { + return true; + } + return false; + }); + + home = mountWithIntl(); + // alerts now too! + expect(home.find('.euiTab__content').length).toBe(3); + }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/home.tsx b/x-pack/plugins/triggers_actions_ui/public/application/home.tsx index 1489397e1e7e3..9110ebe1f51c8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/home.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/home.tsx @@ -6,11 +6,12 @@ */ import React, { lazy, useEffect } from 'react'; -import { Route, RouteComponentProps, Switch } from 'react-router-dom'; +import { Route, RouteComponentProps, Switch, Redirect } from 'react-router-dom'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiSpacer, EuiButtonEmpty, EuiPageHeader } from '@elastic/eui'; -import { Section, routeToConnectors, routeToRules } from './constants'; +import { getIsExperimentalFeatureEnabled } from '../common/get_experimental_features'; +import { Section, routeToConnectors, routeToRules, routeToInternalAlerts } from './constants'; import { getAlertingSectionBreadcrumb } from './lib/breadcrumb'; import { getCurrentDocTitle } from './lib/doc_title'; import { hasShowActionsCapability } from './lib/capabilities'; @@ -24,6 +25,7 @@ const ActionsConnectorsList = lazy( () => import('./sections/actions_connectors_list/components/actions_connectors_list') ); const RulesList = lazy(() => import('./sections/rules_list/components/rules_list')); +const AlertsPage = lazy(() => import('./sections/alerts_table/alerts_page')); export interface MatchParams { section: Section; @@ -38,9 +40,11 @@ export const TriggersActionsUIHome: React.FunctionComponent + ), + }); + } + const onSectionChange = (newSection: Section) => { history.push(`/${newSection}`); }; @@ -134,6 +150,15 @@ export const TriggersActionsUIHome: React.FunctionComponent + {isInternalAlertsTableEnabled ? ( + + ) : ( + + )} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_page/README.md b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_page/README.md new file mode 100644 index 0000000000000..6e57a57f7abdf --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_page/README.md @@ -0,0 +1 @@ +Note: This entire folder is meant for internal, testing purposes and is not exposed to users. \ No newline at end of file diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_page/alerts_page.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_page/alerts_page.tsx new file mode 100644 index 0000000000000..c3c02dd064bcb --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_page/alerts_page.tsx @@ -0,0 +1,195 @@ +/* + * 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 React, { useState, useCallback, useEffect } from 'react'; +import { get } from 'lodash'; +import { + EuiDataGridCellValueElementProps, + EuiDataGridControlColumn, + EuiFlexItem, + EuiFlexGroup, + EuiSpacer, + EuiProgress, +} from '@elastic/eui'; +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { AlertConsumers } from '@kbn/rule-data-utils'; +import { + RuleRegistrySearchRequest, + RuleRegistrySearchResponse, + RuleRegistrySearchRequestPagination, +} from '../../../../../../rule_registry/common'; +import { AlertsTable } from '../alerts_table'; +import { useKibana } from '../../../../common/lib/kibana'; +import { AbortError } from '../../../../../../../../src/plugins/kibana_utils/common'; +import { AlertsData } from '../../../../types'; + +const consumers = [ + AlertConsumers.APM, + AlertConsumers.LOGS, + AlertConsumers.UPTIME, + AlertConsumers.INFRASTRUCTURE, +]; + +const defaultPagination = { + pageSize: 10, + pageIndex: 0, +}; + +const defaultSort: estypes.SortCombinations[] = [ + { + 'event.action': { + order: 'asc', + }, + }, +]; + +const AlertsPage: React.FunctionComponent = () => { + const { data, notifications } = useKibana().services; + const [showCheckboxes] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [isInitializing, setIsInitializing] = useState(true); + const [alertsCount, setAlertsCount] = useState(0); + const [alerts, setAlerts] = useState([]); + const [sort, setSort] = useState(defaultSort); + const [pagination, setPagination] = useState(defaultPagination); + + const onPageChange = (_pagination: RuleRegistrySearchRequestPagination) => { + setPagination(_pagination); + }; + const onSortChange = (_sort: Array<{ id: string; direction: 'asc' | 'desc' }>) => { + setSort( + _sort.map(({ id, direction }) => { + return { + [id]: { + order: direction, + }, + }; + }) + ); + }; + + const asyncSearch = useCallback(() => { + setIsLoading(true); + const abortController = new AbortController(); + const request: RuleRegistrySearchRequest = { + featureIds: consumers, + sort, + pagination, + }; + data.search + .search(request, { + strategy: 'privateRuleRegistryAlertsSearchStrategy', + abortSignal: abortController.signal, + }) + .subscribe({ + next: (res) => { + const alertsResponse = res.rawResponse.hits.hits.map( + (hit) => hit.fields as unknown as AlertsData + ) as AlertsData[]; + setAlerts(alertsResponse); + const total = !isNaN(res.rawResponse.hits.total as number) + ? (res.rawResponse.hits.total as number) + : (res.rawResponse.hits.total as estypes.SearchTotalHits).value ?? 0; + setAlertsCount(total); + setIsLoading(false); + }, + error: (e) => { + if (e instanceof AbortError) { + notifications.toasts.addWarning({ + title: e.message, + }); + } else { + notifications.toasts.addDanger({ + title: 'Failed to run search', + text: e.message, + }); + } + setIsLoading(false); + }, + }); + setIsInitializing(false); + }, [data.search, notifications.toasts, sort, pagination]); + + useEffect(() => { + asyncSearch(); + }, [asyncSearch]); + + const useFetchAlertsData = () => { + return { + activePage: pagination.pageIndex, + alerts, + alertsCount, + isInitializing, + isLoading, + getInspectQuery: () => ({ request: {}, response: {} }), + onColumnsChange: (columns: EuiDataGridControlColumn[]) => {}, + onPageChange, + onSortChange, + refresh: () => { + asyncSearch(); + }, + }; + }; + + const tableProps = { + consumers, + bulkActions: [], + columns: [ + { + id: 'event.action', + displayAsText: 'Alert status', + initialWidth: 150, + }, + { + id: '@timestamp', + displayAsText: 'Last updated', + initialWidth: 250, + }, + { + id: 'kibana.alert.duration.us', + displayAsText: 'Duration', + initialWidth: 150, + }, + { + id: 'kibana.alert.reason', + displayAsText: 'Reason', + }, + ], + deletedEventIds: [], + disabledCellActions: [], + pageSize: defaultPagination.pageSize, + pageSizeOptions: [2, 5, 10, 20, 50, 100], + leadingControlColumns: [], + renderCellValue: (rcvProps: EuiDataGridCellValueElementProps) => { + const { columnId, visibleRowIndex } = rcvProps as EuiDataGridCellValueElementProps & { + visibleRowIndex: number; + }; + const value = (get(alerts[visibleRowIndex], columnId) ?? [])[0]; + return value ?? 'N/A'; + }, + showCheckboxes, + trailingControlColumns: [], + useFetchAlertsData, + 'data-test-subj': 'internalAlertsPage', + }; + + return ( +
+

THIS IS AN INTERNAL TEST PAGE

+ + + + {isLoading && ( + + )} + + + +
+ ); +}; + +export { AlertsPage }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_page/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_page/index.ts new file mode 100644 index 0000000000000..c574bda7f14ff --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_page/index.ts @@ -0,0 +1,9 @@ +/* + * 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 { AlertsPage } from './alerts_page'; +// eslint-disable-next-line import/no-default-export +export { AlertsPage as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx new file mode 100644 index 0000000000000..c0a50e460fe8a --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx @@ -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 React from 'react'; +import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; +import { act } from 'react-dom/test-utils'; +import { AlertConsumers } from '@kbn/rule-data-utils'; +import { AlertsTable } from './alerts_table'; +import { AlertsData } from '../../../types'; +jest.mock('../../../../../../../src/plugins/data/public/'); +jest.mock('../../../common/lib/kibana'); + +describe('AlertsTable', () => { + const consumers = [ + AlertConsumers.APM, + AlertConsumers.LOGS, + AlertConsumers.UPTIME, + AlertConsumers.INFRASTRUCTURE, + AlertConsumers.SIEM, + ]; + const columns = [ + { + id: 'kibana.alert.rule.name', + displayAsText: 'Name', + }, + { + id: 'kibana.alert.rule.category', + displayAsText: 'Category', + }, + ]; + + const alerts: AlertsData[] = [ + { + field1: ['one'], + field2: ['two'], + }, + { + field1: ['three'], + field2: ['four'], + }, + ]; + const fetchAlertsData = { + activePage: 0, + alerts, + alertsCount: alerts.length, + isInitializing: false, + isLoading: false, + getInspectQuery: jest.fn().mockImplementation(() => ({ request: {}, response: {} })), + onColumnsChange: jest.fn(), + onPageChange: jest.fn(), + onSortChange: jest.fn(), + refresh: jest.fn(), + }; + const useFetchAlertsData = () => { + return fetchAlertsData; + }; + + const tableProps = { + consumers, + bulkActions: [], + columns, + deletedEventIds: [], + disabledCellActions: [], + pageSize: 1, + pageSizeOptions: [1, 2, 5, 10, 20, 50, 100], + leadingControlColumns: [], + renderCellValue: jest.fn().mockImplementation((props) => { + return `${props.colIndex}:${props.rowIndex}`; + }), + showCheckboxes: false, + trailingControlColumns: [], + useFetchAlertsData, + 'data-test-subj': 'testTable', + }; + + it('should support sorting', async () => { + const wrapper = mountWithIntl(); + await act(async () => { + await nextTick(); + wrapper.update(); + }); + wrapper.find('.euiDataGridHeaderCell__button').first().simulate('click'); + wrapper.update(); + wrapper + .find(`[data-test-subj="dataGridHeaderCellActionGroup-${columns[0].id}"]`) + .first() + .simulate('click'); + wrapper.find(`.euiListGroupItem__label[title="Sort A-Z"]`).simulate('click'); + expect(fetchAlertsData.onSortChange).toHaveBeenCalledWith([ + { direction: 'asc', id: 'kibana.alert.rule.name' }, + ]); + }); + + it('should support pagination', async () => { + const wrapper = mountWithIntl(); + await act(async () => { + await nextTick(); + wrapper.update(); + }); + wrapper.find('.euiPagination__item EuiButtonEmpty').at(1).simulate('click'); + expect(fetchAlertsData.onPageChange).toHaveBeenCalledWith({ pageIndex: 1, pageSize: 1 }); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx new file mode 100644 index 0000000000000..2568c5b96407b --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx @@ -0,0 +1,46 @@ +/* + * 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 React, { useState } from 'react'; +import { EuiDataGrid } from '@elastic/eui'; +import { useSorting, usePagination } from './hooks'; +import { AlertsTableProps } from '../../../types'; + +const AlertsTable: React.FunctionComponent = (props: AlertsTableProps) => { + const { activePage, alertsCount, onPageChange, onSortChange } = props.useFetchAlertsData(); + const { sortingColumns, onSort } = useSorting(onSortChange); + const { pagination, onChangePageSize, onChangePageIndex } = usePagination({ + onPageChange, + pageIndex: activePage, + pageSize: props.pageSize, + }); + + const [visibleColumns, setVisibleColumns] = useState(props.columns.map(({ id }) => id)); + + return ( +
+ +
+ ); +}; + +export { AlertsTable }; +// eslint-disable-next-line import/no-default-export +export { AlertsTable as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/index.ts new file mode 100644 index 0000000000000..9da3178db8c39 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/index.ts @@ -0,0 +1,8 @@ +/* + * 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 { usePagination } from './use_pagination'; +export { useSorting } from './use_sorting'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_pagination.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_pagination.test.ts new file mode 100644 index 0000000000000..8b8ff68f106c2 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_pagination.test.ts @@ -0,0 +1,45 @@ +/* + * 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 { usePagination } from './use_pagination'; +import { renderHook, act } from '@testing-library/react-hooks'; + +describe('usePagination', () => { + const onPageChange = jest.fn(); + const pageIndex = 0; + const pageSize = 10; + + beforeEach(() => { + onPageChange.mockClear(); + }); + + it('should return the pagination information and callback functions', () => { + const { result } = renderHook(() => usePagination({ onPageChange, pageIndex, pageSize })); + expect(result.current.pagination).toStrictEqual({ pageIndex, pageSize }); + expect(result.current.onChangePageSize).toBeDefined(); + expect(result.current.onChangePageIndex).toBeDefined(); + }); + + it('should change the pagination when `onChangePageSize` is called', () => { + const { result } = renderHook(() => usePagination({ onPageChange, pageIndex, pageSize })); + + act(() => { + result.current.onChangePageSize(20); + }); + + expect(result.current.pagination).toStrictEqual({ pageIndex, pageSize: 20 }); + }); + + it('should change the pagination when `onChangePageIndex` is called', () => { + const { result } = renderHook(() => usePagination({ onPageChange, pageIndex, pageSize })); + + act(() => { + result.current.onChangePageIndex(1); + }); + + expect(result.current.pagination).toStrictEqual({ pageIndex: 1, pageSize }); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_pagination.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_pagination.ts new file mode 100644 index 0000000000000..4f00e05ed1122 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_pagination.ts @@ -0,0 +1,38 @@ +/* + * 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 { useCallback, useState } from 'react'; +import { RuleRegistrySearchRequestPagination } from '../../../../../../rule_registry/common'; + +type PaginationProps = RuleRegistrySearchRequestPagination & { + onPageChange: (pagination: RuleRegistrySearchRequestPagination) => void; +}; + +export function usePagination({ onPageChange, pageIndex, pageSize }: PaginationProps) { + const [pagination, setPagination] = useState({ + pageIndex, + pageSize, + }); + const onChangePageSize = useCallback( + (_pageSize) => { + setPagination((state) => ({ + ...state, + pageSize: _pageSize, + pageIndex: 0, + })); + onPageChange({ pageIndex: 0, pageSize: _pageSize }); + }, + [setPagination, onPageChange] + ); + const onChangePageIndex = useCallback( + (_pageIndex) => { + setPagination((state) => ({ ...state, pageIndex: _pageIndex })); + onPageChange({ pageIndex: _pageIndex, pageSize: pagination.pageSize }); + }, + [setPagination, onPageChange, pagination.pageSize] + ); + return { pagination, onChangePageSize, onChangePageIndex }; +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_sorting.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_sorting.test.ts new file mode 100644 index 0000000000000..487f6908a334e --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_sorting.test.ts @@ -0,0 +1,33 @@ +/* + * 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 { useSorting } from './use_sorting'; +import { renderHook, act } from '@testing-library/react-hooks'; + +describe('useSorting', () => { + const onSortChange = jest.fn(); + + beforeEach(() => { + onSortChange.mockClear(); + }); + + it('should return the sorted columns and the callback function to call when sort changes', () => { + const { result } = renderHook(() => useSorting(onSortChange)); + expect(result.current.sortingColumns).toStrictEqual([]); + expect(result.current.onSort).toBeDefined(); + }); + + it('should change the columns when `onSort` is called', () => { + const { result } = renderHook(() => useSorting(onSortChange)); + + act(() => { + result.current.onSort([{ id: 'field', direction: 'asc' }]); + }); + + expect(onSortChange).toHaveBeenCalledWith([{ direction: 'asc', id: 'field' }]); + expect(result.current.sortingColumns).toStrictEqual([{ direction: 'asc', id: 'field' }]); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_sorting.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_sorting.ts new file mode 100644 index 0000000000000..cbb84c95806a0 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_sorting.ts @@ -0,0 +1,21 @@ +/* + * 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 { useCallback, useState } from 'react'; + +export function useSorting( + onSortChange: (sort: Array<{ id: string; direction: 'asc' | 'desc' }>) => void +) { + const [sortingColumns, setSortingColumns] = useState([]); + const onSort = useCallback( + (_state) => { + onSortChange(_state); + setSortingColumns(_state); + }, + [setSortingColumns, onSortChange] + ); + return { sortingColumns, onSort }; +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/index.ts new file mode 100644 index 0000000000000..7fec39ad65d57 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/index.ts @@ -0,0 +1,10 @@ +/* + * 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 { lazy } from 'react'; +import { suspendedComponentWithProps } from '../../lib/suspended_component_with_props'; + +export const AlertsTable = suspendedComponentWithProps(lazy(() => import('./alerts_table'))); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/types.ts new file mode 100644 index 0000000000000..9622477db3a30 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/types.ts @@ -0,0 +1,12 @@ +/* + * 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 { AlertConsumers } from '@kbn/rule-data-utils'; + +export interface Consumer { + id: AlertConsumers; + name: string; +} diff --git a/x-pack/plugins/triggers_actions_ui/public/common/get_alerts_table.tsx b/x-pack/plugins/triggers_actions_ui/public/common/get_alerts_table.tsx new file mode 100644 index 0000000000000..4c2727cd9976d --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/common/get_alerts_table.tsx @@ -0,0 +1,14 @@ +/* + * 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 React from 'react'; +import { AlertsTable } from '../application/sections/alerts_table'; +import type { AlertsTableProps } from '../types'; + +export const getAlertsTableLazy = (props: AlertsTableProps) => { + return ; +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/get_experimental_features.test.tsx b/x-pack/plugins/triggers_actions_ui/public/common/get_experimental_features.test.tsx index 0b865eb5b61be..aa7de97a6c889 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/get_experimental_features.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/common/get_experimental_features.test.tsx @@ -5,14 +5,18 @@ * 2.0. */ +import { getExperimentalAllowedValues } from '../../common/experimental_features'; import { ExperimentalFeaturesService } from './experimental_features_service'; import { getIsExperimentalFeatureEnabled } from './get_experimental_features'; +const allowedExperimentalValueKeys = getExperimentalAllowedValues(); + describe('getIsExperimentalFeatureEnabled', () => { it('getIsExperimentalFeatureEnabled returns the flag enablement', async () => { ExperimentalFeaturesService.init({ experimentalFeatures: { rulesListDatagrid: true, + internalAlertsTable: true, rulesDetailLogs: true, }, }); @@ -25,8 +29,14 @@ describe('getIsExperimentalFeatureEnabled', () => { expect(result).toEqual(true); + result = getIsExperimentalFeatureEnabled('internalAlertsTable'); + + expect(result).toEqual(true); + expect(() => getIsExperimentalFeatureEnabled('doesNotExist' as any)).toThrowError( - 'Invalid enable value doesNotExist. Allowed values are: rulesListDatagrid, rulesDetailLogs' + `Invalid enable value doesNotExist. Allowed values are: ${allowedExperimentalValueKeys.join( + ', ' + )}` ); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/common/get_experimental_features.ts b/x-pack/plugins/triggers_actions_ui/public/common/get_experimental_features.ts index 706af60579711..41a321bbd2981 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/get_experimental_features.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/get_experimental_features.ts @@ -7,10 +7,10 @@ import { ExperimentalFeatures, - allowedExperimentalValues, isValidExperimentalValue, getExperimentalAllowedValues, } from '../../common/experimental_features'; +import { ExperimentalFeaturesService } from './experimental_features_service'; const allowedExperimentalValueKeys = getExperimentalAllowedValues(); @@ -23,5 +23,5 @@ export const getIsExperimentalFeatureEnabled = (feature: keyof ExperimentalFeatu ); } - return allowedExperimentalValues[feature]; + return ExperimentalFeaturesService.get()[feature]; }; diff --git a/x-pack/plugins/triggers_actions_ui/public/mocks.ts b/x-pack/plugins/triggers_actions_ui/public/mocks.ts index 7a0420594118b..706a66963e22f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/mocks.ts +++ b/x-pack/plugins/triggers_actions_ui/public/mocks.ts @@ -20,7 +20,9 @@ import { RuleTypeModel, ConnectorAddFlyoutProps, ConnectorEditFlyoutProps, + AlertsTableProps, } from './types'; +import { getAlertsTableLazy } from './common/get_alerts_table'; function createStartMock(): TriggersAndActionsUIPublicPluginStart { const actionTypeRegistry = new TypeRegistry(); @@ -51,6 +53,9 @@ function createStartMock(): TriggersAndActionsUIPublicPluginStart { ruleTypeRegistry, }); }, + getAlertsTable: (props: AlertsTableProps) => { + return getAlertsTableLazy(props); + }, }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts index afeff4ba8364e..1993f0f931e17 100644 --- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts +++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts @@ -32,6 +32,7 @@ import { getAddConnectorFlyoutLazy } from './common/get_add_connector_flyout'; import { getEditConnectorFlyoutLazy } from './common/get_edit_connector_flyout'; import { getAddAlertFlyoutLazy } from './common/get_add_alert_flyout'; import { getEditAlertFlyoutLazy } from './common/get_edit_alert_flyout'; +import { getAlertsTableLazy } from './common/get_alerts_table'; import { ExperimentalFeaturesService } from './common/experimental_features_service'; import { ExperimentalFeatures, @@ -45,6 +46,7 @@ import type { RuleTypeModel, ConnectorAddFlyoutProps, ConnectorEditFlyoutProps, + AlertsTableProps, } from './types'; import { TriggersActionsUiConfigType } from '../common/types'; @@ -68,6 +70,7 @@ export interface TriggersAndActionsUIPublicPluginStart { getEditAlertFlyout: ( props: Omit ) => ReactElement; + getAlertsTable: (props: AlertsTableProps) => ReactElement; } interface PluginsSetup { @@ -215,6 +218,9 @@ export class Plugin ruleTypeRegistry: this.ruleTypeRegistry, }); }, + getAlertsTable: (props: AlertsTableProps) => { + return getAlertsTableLazy(props); + }, }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 7a1efaed33abf..c459d118c69b9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -8,9 +8,15 @@ import type { PublicMethodsOf } from '@kbn/utility-types'; import type { DocLinksStart } from 'kibana/public'; import { ComponentType } from 'react'; +import { AlertConsumers } from '@kbn/rule-data-utils'; import { ChartsPluginSetup } from 'src/plugins/charts/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; -import { IconType } from '@elastic/eui'; +import { + IconType, + EuiDataGridColumn, + EuiDataGridControlColumn, + EuiDataGridCellValueElementProps, +} from '@elastic/eui'; import { ActionType, AlertHistoryEsIndexConnectorId, @@ -38,6 +44,7 @@ import { ActionVariable, RuleType as CommonRuleType, } from '../../alerting/common'; +import { RuleRegistrySearchRequestPagination } from '../../rule_registry/common'; // In Triggers and Actions we treat all `Alert`s as `SanitizedRule` // so the `Params` is a black-box of Record @@ -355,3 +362,41 @@ export interface TriggersActionsUiConfig { enforce: boolean; }; } + +export type AlertsData = Record; + +export interface FetchAlertData { + activePage: number; + alerts: AlertsData[]; + alertsCount: number; + isInitializing: boolean; + isLoading: boolean; + getInspectQuery: () => { request: {}; response: {} }; + onColumnsChange: (columns: EuiDataGridControlColumn[]) => void; + onPageChange: (pagination: RuleRegistrySearchRequestPagination) => void; + onSortChange: (sort: Array<{ id: string; direction: 'asc' | 'desc' }>) => void; + refresh: () => void; +} + +export interface BulkActionsObjectProp { + alertStatusActions?: boolean; + onAlertStatusActionSuccess?: void; + onAlertStatusActionFailure?: void; +} + +export interface AlertsTableProps { + consumers: AlertConsumers[]; + bulkActions: BulkActionsObjectProp; + columns: EuiDataGridColumn[]; + // defaultCellActions: TGridCellAction[]; + deletedEventIds: string[]; + disabledCellActions: string[]; + pageSize: number; + pageSizeOptions: number[]; + leadingControlColumns: EuiDataGridControlColumn[]; + renderCellValue: (props: EuiDataGridCellValueElementProps) => React.ReactNode; + showCheckboxes: boolean; + trailingControlColumns: EuiDataGridControlColumn[]; + useFetchAlertsData: () => FetchAlertData; + 'data-test-subj': string; +} diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_table.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_table.ts new file mode 100644 index 0000000000000..f0797ffd54d91 --- /dev/null +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_table.ts @@ -0,0 +1,157 @@ +/* + * 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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common', 'triggersActionsUI', 'header']); + const retry = getService('retry'); + const esArchiver = getService('esArchiver'); + const find = getService('find'); + + describe('Alerts table', function () { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/observability/alerts'); + }); + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/observability/alerts'); + }); + + it('should load the table', async () => { + await PageObjects.common.navigateToUrlWithBrowserHistory('triggersActions', '/alerts'); + const headingText = await PageObjects.triggersActionsUI.getSectionHeadingText(); + expect(headingText).to.be('Rules and Connectors'); + + await waitTableIsLoaded(); + + const euiDataGridRows = await find.allByCssSelector('.euiDataGridRow'); + const rows = []; + for (const euiDataGridRow of euiDataGridRows) { + const $ = await euiDataGridRow.parseDomContent(); + rows.push({ + status: $.findTestSubjects('dataGridRowCell') + .find('[data-gridcell-column-id="event.action"] .euiDataGridRowCell__truncate') + .text(), + lastUpdated: $.findTestSubjects('dataGridRowCell') + .find('[data-gridcell-column-id="@timestamp"] .euiDataGridRowCell__truncate') + .text(), + duration: $.findTestSubjects('dataGridRowCell') + .find( + '[data-gridcell-column-id="kibana.alert.duration.us"] .euiDataGridRowCell__truncate' + ) + .text(), + reason: $.findTestSubjects('dataGridRowCell') + .find('[data-gridcell-column-id="kibana.alert.reason"] .euiDataGridRowCell__truncate') + .text(), + }); + } + expect(rows.length).to.be(10); + expect(rows[0].status).to.be('active'); + expect(rows[0].lastUpdated).to.be('2021-10-19T15:20:38.749Z'); + expect(rows[0].duration).to.be('1197194000'); + expect(rows[0].reason).to.be( + 'Failed transactions rate is greater than 5.0% (current value is 31%) for elastic-co-frontend' + ); + }); + + it('should sort properly', async () => { + await PageObjects.common.navigateToUrlWithBrowserHistory('triggersActions', '/alerts'); + const headingText = await PageObjects.triggersActionsUI.getSectionHeadingText(); + expect(headingText).to.be('Rules and Connectors'); + + await waitTableIsLoaded(); + + await find.clickDisplayedByCssSelector( + '[data-test-subj="dataGridHeaderCell-event.action"] .euiDataGridHeaderCell__button' + ); + + const popoverButtons = await testSubjects.find('dataGridHeaderCellActionGroup-event.action'); + await (await popoverButtons.findAllByCssSelector('.euiListGroupItem__button'))[2].click(); + + await waitTableIsLoaded(); + + const euiDataGridRows = await find.allByCssSelector('.euiDataGridRow'); + const rows = []; + for (const euiDataGridRow of euiDataGridRows) { + const $ = await euiDataGridRow.parseDomContent(); + rows.push({ + status: $.findTestSubjects('dataGridRowCell') + .find('[data-gridcell-column-id="event.action"] .euiDataGridRowCell__truncate') + .text(), + lastUpdated: $.findTestSubjects('dataGridRowCell') + .find('[data-gridcell-column-id="@timestamp"] .euiDataGridRowCell__truncate') + .text(), + duration: $.findTestSubjects('dataGridRowCell') + .find( + '[data-gridcell-column-id="kibana.alert.duration.us"] .euiDataGridRowCell__truncate' + ) + .text(), + reason: $.findTestSubjects('dataGridRowCell') + .find('[data-gridcell-column-id="kibana.alert.reason"] .euiDataGridRowCell__truncate') + .text(), + }); + } + expect(rows.length).to.be(10); + expect(rows[0].status).to.be('open'); + expect(rows[0].lastUpdated).to.be('2021-10-19T15:20:26.974Z'); + expect(rows[0].duration).to.be('0'); + expect(rows[0].reason).to.be( + 'CPU usage is greater than a threshold of 40 (current value is 45.2%) for gke-edge-oblt-default-pool-350b44de-c3dd' + ); + }); + + it('should paginate properly', async () => { + await PageObjects.common.navigateToUrlWithBrowserHistory('triggersActions', '/alerts'); + const headingText = await PageObjects.triggersActionsUI.getSectionHeadingText(); + expect(headingText).to.be('Rules and Connectors'); + + await waitTableIsLoaded(); + + await testSubjects.click('pagination-button-1'); + + await waitTableIsLoaded(); + + const euiDataGridRows = await find.allByCssSelector('.euiDataGridRow'); + const rows = []; + for (const euiDataGridRow of euiDataGridRows) { + const $ = await euiDataGridRow.parseDomContent(); + rows.push({ + status: $.findTestSubjects('dataGridRowCell') + .find('[data-gridcell-column-id="event.action"] .euiDataGridRowCell__truncate') + .text(), + lastUpdated: $.findTestSubjects('dataGridRowCell') + .find('[data-gridcell-column-id="@timestamp"] .euiDataGridRowCell__truncate') + .text(), + duration: $.findTestSubjects('dataGridRowCell') + .find( + '[data-gridcell-column-id="kibana.alert.duration.us"] .euiDataGridRowCell__truncate' + ) + .text(), + reason: $.findTestSubjects('dataGridRowCell') + .find('[data-gridcell-column-id="kibana.alert.reason"] .euiDataGridRowCell__truncate') + .text(), + }); + } + expect(rows.length).to.be(10); + expect(rows[0].status).to.be('active'); + expect(rows[0].lastUpdated).to.be('2021-10-19T15:20:26.974Z'); + expect(rows[0].duration).to.be('63291000'); + expect(rows[0].reason).to.be( + 'CPU usage is greater than a threshold of 40 (current value is 40.8%) for gke-edge-oblt-default-pool-350b44de-3v4p' + ); + }); + + async function waitTableIsLoaded() { + return await retry.try(async () => { + const exists = await testSubjects.exists('internalAlertsPageLoading'); + if (exists) throw new Error('Still loading...'); + }); + } + }); +}; diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/index.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/index.ts index e70d4fdc96f61..76a952c354ab4 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/index.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/index.ts @@ -15,5 +15,6 @@ export default ({ loadTestFile, getService }: FtrProviderContext) => { loadTestFile(require.resolve('./alert_create_flyout')); loadTestFile(require.resolve('./details')); loadTestFile(require.resolve('./connectors')); + loadTestFile(require.resolve('./alerts_table')); }); }; diff --git a/x-pack/test/functional_with_es_ssl/config.ts b/x-pack/test/functional_with_es_ssl/config.ts index b2b6735a99c8b..dc2f6b8618ce3 100644 --- a/x-pack/test/functional_with_es_ssl/config.ts +++ b/x-pack/test/functional_with_es_ssl/config.ts @@ -67,6 +67,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { `--elasticsearch.hosts=https://${servers.elasticsearch.hostname}:${servers.elasticsearch.port}`, `--elasticsearch.ssl.certificateAuthorities=${CA_CERT_PATH}`, `--plugin-path=${join(__dirname, 'fixtures', 'plugins', 'alerts')}`, + `--xpack.trigger_actions_ui.enableExperimental=${JSON.stringify(['internalAlertsTable'])}`, `--xpack.alerting.rules.minimumScheduleInterval.value="2s"`, `--xpack.actions.enabledActionTypes=${JSON.stringify(enabledActionTypes)}`, `--xpack.actions.preconfiguredAlertHistoryEsIndex=false`, From 74450ad697f3a770f38a5123d400e0077cfe124b Mon Sep 17 00:00:00 2001 From: Candace Park <56409205+parkiino@users.noreply.github.com> Date: Tue, 29 Mar 2022 11:02:09 -0400 Subject: [PATCH 26/37] [Security Solution][Endpoint][Admin][Policy] Enable Policy list feature flag (#128698) --- .../plugins/security_solution/common/experimental_features.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index 3a932238f3a34..a9cf3e4a8132e 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -24,7 +24,7 @@ export const allowedExperimentalValues = Object.freeze({ riskyUsersEnabled: false, securityRulesCancelEnabled: false, pendingActionResponsesWithAck: true, - policyListEnabled: false, + policyListEnabled: true, /** * This is used for enabling the end to end tests for the security_solution telemetry. From e906db25664b70bded74c2c7233abc9b15d67e68 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Tue, 29 Mar 2022 11:03:04 -0400 Subject: [PATCH 27/37] [Fleet] Merge settings & mappings component template in @package (#128498) --- .../fleet/server/constants/fleet_es_assets.ts | 3 +- .../plugins/fleet/server/constants/index.ts | 3 +- .../epm/elasticsearch/template/install.ts | 34 +- .../epm/__snapshots__/install_by_upload.snap | 735 ------------------ .../apis/epm/install_by_upload.ts | 4 +- .../apis/epm/install_overrides.ts | 23 +- .../apis/epm/install_remove_assets.ts | 45 +- .../apis/epm/update_assets.ts | 69 +- 8 files changed, 52 insertions(+), 864 deletions(-) delete mode 100644 x-pack/test/fleet_api_integration/apis/epm/__snapshots__/install_by_upload.snap diff --git a/x-pack/plugins/fleet/server/constants/fleet_es_assets.ts b/x-pack/plugins/fleet/server/constants/fleet_es_assets.ts index 859a25a0ec7c7..56aaf450d46e9 100644 --- a/x-pack/plugins/fleet/server/constants/fleet_es_assets.ts +++ b/x-pack/plugins/fleet/server/constants/fleet_es_assets.ts @@ -8,9 +8,8 @@ import { getESAssetMetadata } from '../services/epm/elasticsearch/meta'; const meta = getESAssetMetadata(); -export const MAPPINGS_TEMPLATE_SUFFIX = '@mappings'; -export const SETTINGS_TEMPLATE_SUFFIX = '@settings'; +export const PACKAGE_TEMPLATE_SUFFIX = '@package'; export const USER_SETTINGS_TEMPLATE_SUFFIX = '@custom'; diff --git a/x-pack/plugins/fleet/server/constants/index.ts b/x-pack/plugins/fleet/server/constants/index.ts index ec7a4a2664882..813f381ecc4fc 100644 --- a/x-pack/plugins/fleet/server/constants/index.ts +++ b/x-pack/plugins/fleet/server/constants/index.ts @@ -66,7 +66,6 @@ export { FLEET_FINAL_PIPELINE_ID, FLEET_FINAL_PIPELINE_CONTENT, FLEET_FINAL_PIPELINE_VERSION, - MAPPINGS_TEMPLATE_SUFFIX, - SETTINGS_TEMPLATE_SUFFIX, USER_SETTINGS_TEMPLATE_SUFFIX, + PACKAGE_TEMPLATE_SUFFIX, } from './fleet_es_assets'; diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.ts index f30971c0e7d5e..a77f843aef2d3 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { merge, cloneDeep } from 'lodash'; +import { merge } from 'lodash'; import Boom from '@hapi/boom'; import type { ElasticsearchClient, Logger, SavedObjectsClientContract } from 'src/core/server'; @@ -29,8 +29,7 @@ import { getAsset, getPathParts } from '../../archive'; import { removeAssetTypesFromInstalledEs, saveInstalledEsRefs } from '../../packages/install'; import { FLEET_COMPONENT_TEMPLATES, - MAPPINGS_TEMPLATE_SUFFIX, - SETTINGS_TEMPLATE_SUFFIX, + PACKAGE_TEMPLATE_SUFFIX, USER_SETTINGS_TEMPLATE_SUFFIX, } from '../../../../constants'; @@ -245,34 +244,28 @@ function buildComponentTemplates(params: { defaultSettings: IndexTemplate['template']['settings']; }) { const { templateName, registryElasticsearch, packageName, defaultSettings, mappings } = params; - const mappingsTemplateName = `${templateName}${MAPPINGS_TEMPLATE_SUFFIX}`; - const settingsTemplateName = `${templateName}${SETTINGS_TEMPLATE_SUFFIX}`; + const packageTemplateName = `${templateName}${PACKAGE_TEMPLATE_SUFFIX}`; const userSettingsTemplateName = `${templateName}${USER_SETTINGS_TEMPLATE_SUFFIX}`; const templatesMap: TemplateMap = {}; const _meta = getESAssetMetadata({ packageName }); const indexTemplateSettings = registryElasticsearch?.['index_template.settings'] ?? {}; - // @ts-expect-error no property .mapping (yes there is) - const indexTemplateMappingSettings = indexTemplateSettings?.index?.mapping; - const indexTemplateSettingsForTemplate = cloneDeep(indexTemplateSettings); - - // index.mapping settings must go on the mapping component template otherwise - // the template may be rejected e.g if nested_fields.limit has been increased - if (indexTemplateMappingSettings) { - // @ts-expect-error no property .mapping - delete indexTemplateSettingsForTemplate.index.mapping; - } - templatesMap[mappingsTemplateName] = { + const templateSettings = merge(defaultSettings, indexTemplateSettings); + + templatesMap[packageTemplateName] = { template: { settings: { + ...templateSettings, index: { + ...templateSettings.index, mapping: { + ...templateSettings?.mapping, total_fields: { + ...templateSettings?.mapping?.total_fields, limit: '10000', }, - ...indexTemplateMappingSettings, }, }, }, @@ -281,13 +274,6 @@ function buildComponentTemplates(params: { _meta, }; - templatesMap[settingsTemplateName] = { - template: { - settings: merge(defaultSettings, indexTemplateSettingsForTemplate), - }, - _meta, - }; - // return empty/stub template templatesMap[userSettingsTemplateName] = { template: { diff --git a/x-pack/test/fleet_api_integration/apis/epm/__snapshots__/install_by_upload.snap b/x-pack/test/fleet_api_integration/apis/epm/__snapshots__/install_by_upload.snap deleted file mode 100644 index 421a5fbdf1744..0000000000000 --- a/x-pack/test/fleet_api_integration/apis/epm/__snapshots__/install_by_upload.snap +++ /dev/null @@ -1,735 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Fleet Endpoints EPM Endpoints installs packages from direct upload should install a zip archive correctly and package info should return correctly after validation 1`] = ` -Object { - "assets": Object { - "elasticsearch": Object { - "ingest_pipeline": Array [ - Object { - "dataset": "access", - "file": "default.yml", - "path": "apache-0.1.4/data_stream/access/elasticsearch/ingest_pipeline/default.yml", - "pkgkey": "apache-0.1.4", - "service": "elasticsearch", - "type": "ingest_pipeline", - }, - Object { - "dataset": "error", - "file": "default.yml", - "path": "apache-0.1.4/data_stream/error/elasticsearch/ingest_pipeline/default.yml", - "pkgkey": "apache-0.1.4", - "service": "elasticsearch", - "type": "ingest_pipeline", - }, - ], - }, - "kibana": Object { - "dashboard": Array [ - Object { - "file": "apache-Logs-Apache-Dashboard-ecs.json", - "path": "apache-0.1.4/kibana/dashboard/apache-Logs-Apache-Dashboard-ecs.json", - "pkgkey": "apache-0.1.4", - "service": "kibana", - "type": "dashboard", - }, - Object { - "file": "apache-Metrics-Apache-HTTPD-server-status-ecs.json", - "path": "apache-0.1.4/kibana/dashboard/apache-Metrics-Apache-HTTPD-server-status-ecs.json", - "pkgkey": "apache-0.1.4", - "service": "kibana", - "type": "dashboard", - }, - ], - "search": Array [ - Object { - "file": "Apache-HTTPD-ecs.json", - "path": "apache-0.1.4/kibana/search/Apache-HTTPD-ecs.json", - "pkgkey": "apache-0.1.4", - "service": "kibana", - "type": "search", - }, - Object { - "file": "Apache-access-logs-ecs.json", - "path": "apache-0.1.4/kibana/search/Apache-access-logs-ecs.json", - "pkgkey": "apache-0.1.4", - "service": "kibana", - "type": "search", - }, - Object { - "file": "Apache-errors-log-ecs.json", - "path": "apache-0.1.4/kibana/search/Apache-errors-log-ecs.json", - "pkgkey": "apache-0.1.4", - "service": "kibana", - "type": "search", - }, - ], - "visualization": Array [ - Object { - "file": "Apache-HTTPD-CPU-ecs.json", - "path": "apache-0.1.4/kibana/visualization/Apache-HTTPD-CPU-ecs.json", - "pkgkey": "apache-0.1.4", - "service": "kibana", - "type": "visualization", - }, - Object { - "file": "Apache-HTTPD-Hostname-list-ecs.json", - "path": "apache-0.1.4/kibana/visualization/Apache-HTTPD-Hostname-list-ecs.json", - "pkgkey": "apache-0.1.4", - "service": "kibana", - "type": "visualization", - }, - Object { - "file": "Apache-HTTPD-Load1-slash-5-slash-15-ecs.json", - "path": "apache-0.1.4/kibana/visualization/Apache-HTTPD-Load1-slash-5-slash-15-ecs.json", - "pkgkey": "apache-0.1.4", - "service": "kibana", - "type": "visualization", - }, - Object { - "file": "Apache-HTTPD-Scoreboard-ecs.json", - "path": "apache-0.1.4/kibana/visualization/Apache-HTTPD-Scoreboard-ecs.json", - "pkgkey": "apache-0.1.4", - "service": "kibana", - "type": "visualization", - }, - Object { - "file": "Apache-HTTPD-Total-accesses-and-kbytes-ecs.json", - "path": "apache-0.1.4/kibana/visualization/Apache-HTTPD-Total-accesses-and-kbytes-ecs.json", - "pkgkey": "apache-0.1.4", - "service": "kibana", - "type": "visualization", - }, - Object { - "file": "Apache-HTTPD-Uptime-ecs.json", - "path": "apache-0.1.4/kibana/visualization/Apache-HTTPD-Uptime-ecs.json", - "pkgkey": "apache-0.1.4", - "service": "kibana", - "type": "visualization", - }, - Object { - "file": "Apache-HTTPD-Workers-ecs.json", - "path": "apache-0.1.4/kibana/visualization/Apache-HTTPD-Workers-ecs.json", - "pkgkey": "apache-0.1.4", - "service": "kibana", - "type": "visualization", - }, - Object { - "file": "Apache-access-unique-IPs-map-ecs.json", - "path": "apache-0.1.4/kibana/visualization/Apache-access-unique-IPs-map-ecs.json", - "pkgkey": "apache-0.1.4", - "service": "kibana", - "type": "visualization", - }, - Object { - "file": "Apache-browsers-ecs.json", - "path": "apache-0.1.4/kibana/visualization/Apache-browsers-ecs.json", - "pkgkey": "apache-0.1.4", - "service": "kibana", - "type": "visualization", - }, - Object { - "file": "Apache-error-logs-over-time-ecs.json", - "path": "apache-0.1.4/kibana/visualization/Apache-error-logs-over-time-ecs.json", - "pkgkey": "apache-0.1.4", - "service": "kibana", - "type": "visualization", - }, - Object { - "file": "Apache-operating-systems-ecs.json", - "path": "apache-0.1.4/kibana/visualization/Apache-operating-systems-ecs.json", - "pkgkey": "apache-0.1.4", - "service": "kibana", - "type": "visualization", - }, - Object { - "file": "Apache-response-codes-of-top-URLs-ecs.json", - "path": "apache-0.1.4/kibana/visualization/Apache-response-codes-of-top-URLs-ecs.json", - "pkgkey": "apache-0.1.4", - "service": "kibana", - "type": "visualization", - }, - Object { - "file": "Apache-response-codes-over-time-ecs.json", - "path": "apache-0.1.4/kibana/visualization/Apache-response-codes-over-time-ecs.json", - "pkgkey": "apache-0.1.4", - "service": "kibana", - "type": "visualization", - }, - ], - }, - }, - "categories": Array [ - "web", - ], - "conditions": Object { - "kibana": Object { - "version": "^7.9.0", - }, - }, - "data_streams": Array [ - Object { - "dataset": "apache.access", - "ingest_pipeline": "default", - "package": "apache", - "path": "access", - "release": "experimental", - "streams": Array [ - Object { - "description": "Collect Apache access logs", - "enabled": true, - "input": "logfile", - "template_path": "log.yml.hbs", - "title": "Apache access logs", - "vars": Array [ - Object { - "default": Array [ - "/var/log/apache2/access.log*", - "/var/log/apache2/other_vhosts_access.log*", - "/var/log/httpd/access_log*", - ], - "multi": true, - "name": "paths", - "required": true, - "show_user": true, - "title": "Paths", - "type": "text", - }, - ], - }, - ], - "title": "Apache access logs", - "type": "logs", - }, - Object { - "dataset": "apache.error", - "ingest_pipeline": "default", - "package": "apache", - "path": "error", - "release": "experimental", - "streams": Array [ - Object { - "description": "Collect Apache error logs", - "enabled": true, - "input": "logfile", - "template_path": "log.yml.hbs", - "title": "Apache error logs", - "vars": Array [ - Object { - "default": Array [ - "/var/log/apache2/error.log*", - "/var/log/httpd/error_log*", - ], - "multi": true, - "name": "paths", - "required": true, - "show_user": true, - "title": "Paths", - "type": "text", - }, - ], - }, - ], - "title": "Apache error logs", - "type": "logs", - }, - Object { - "dataset": "apache.status", - "package": "apache", - "path": "status", - "release": "experimental", - "streams": Array [ - Object { - "description": "Collect Apache status metrics", - "enabled": true, - "input": "apache/metrics", - "template_path": "stream.yml.hbs", - "title": "Apache status metrics", - "vars": Array [ - Object { - "default": "10s", - "multi": false, - "name": "period", - "required": true, - "show_user": true, - "title": "Period", - "type": "text", - }, - Object { - "default": "/server-status", - "multi": false, - "name": "server_status_path", - "required": true, - "show_user": false, - "title": "Server Status Path", - "type": "text", - }, - ], - }, - ], - "title": "Apache status metrics", - "type": "metrics", - }, - ], - "description": "Apache Integration", - "download": "/epr/apache/apache-0.1.4.zip", - "format_version": "1.0.0", - "icons": Array [ - Object { - "path": "/package/apache/0.1.4/img/logo_apache.svg", - "size": "32x32", - "src": "/img/logo_apache.svg", - "title": "Apache Logo", - "type": "image/svg+xml", - }, - ], - "keepPoliciesUpToDate": false, - "license": "basic", - "name": "apache", - "owner": Object { - "github": "elastic/integrations-services", - }, - "path": "/package/apache/0.1.4", - "policy_templates": Array [ - Object { - "description": "Collect logs and metrics from Apache instances", - "inputs": Array [ - Object { - "description": "Collecting Apache access and error logs", - "title": "Collect logs from Apache instances", - "type": "logfile", - }, - Object { - "description": "Collecting Apache status metrics", - "title": "Collect metrics from Apache instances", - "type": "apache/metrics", - "vars": Array [ - Object { - "default": Array [ - "http://127.0.0.1", - ], - "multi": true, - "name": "hosts", - "required": true, - "show_user": true, - "title": "Hosts", - "type": "text", - }, - ], - }, - ], - "multiple": true, - "name": "apache", - "title": "Apache logs and metrics", - }, - ], - "readme": "/package/apache/0.1.4/docs/README.md", - "release": "experimental", - "removable": true, - "savedObject": Object { - "attributes": Object { - "es_index_patterns": Object { - "access": "logs-apache.access-*", - "error": "logs-apache.error-*", - "status": "metrics-apache.status-*", - }, - "install_source": "upload", - "install_status": "installed", - "install_version": "0.1.4", - "installed_es": Array [ - Object { - "id": "logs-apache.access-0.1.4-default", - "type": "ingest_pipeline", - }, - Object { - "id": "logs-apache.error-0.1.4-default", - "type": "ingest_pipeline", - }, - Object { - "id": "logs-apache.access", - "type": "index_template", - }, - Object { - "id": "logs-apache.access@mappings", - "type": "component_template", - }, - Object { - "id": "logs-apache.access@settings", - "type": "component_template", - }, - Object { - "id": "logs-apache.access@custom", - "type": "component_template", - }, - Object { - "id": "metrics-apache.status", - "type": "index_template", - }, - Object { - "id": "metrics-apache.status@mappings", - "type": "component_template", - }, - Object { - "id": "metrics-apache.status@settings", - "type": "component_template", - }, - Object { - "id": "metrics-apache.status@custom", - "type": "component_template", - }, - Object { - "id": "logs-apache.error", - "type": "index_template", - }, - Object { - "id": "logs-apache.error@mappings", - "type": "component_template", - }, - Object { - "id": "logs-apache.error@settings", - "type": "component_template", - }, - Object { - "id": "logs-apache.error@custom", - "type": "component_template", - }, - ], - "installed_kibana": Array [ - Object { - "id": "apache-Logs-Apache-Dashboard-ecs", - "type": "dashboard", - }, - Object { - "id": "apache-Metrics-Apache-HTTPD-server-status-ecs", - "type": "dashboard", - }, - Object { - "id": "Apache-access-unique-IPs-map-ecs", - "type": "visualization", - }, - Object { - "id": "Apache-HTTPD-CPU-ecs", - "type": "visualization", - }, - Object { - "id": "Apache-HTTPD-Load1-slash-5-slash-15-ecs", - "type": "visualization", - }, - Object { - "id": "Apache-response-codes-over-time-ecs", - "type": "visualization", - }, - Object { - "id": "Apache-HTTPD-Workers-ecs", - "type": "visualization", - }, - Object { - "id": "Apache-HTTPD-Hostname-list-ecs", - "type": "visualization", - }, - Object { - "id": "Apache-error-logs-over-time-ecs", - "type": "visualization", - }, - Object { - "id": "Apache-HTTPD-Scoreboard-ecs", - "type": "visualization", - }, - Object { - "id": "Apache-HTTPD-Uptime-ecs", - "type": "visualization", - }, - Object { - "id": "Apache-operating-systems-ecs", - "type": "visualization", - }, - Object { - "id": "Apache-HTTPD-Total-accesses-and-kbytes-ecs", - "type": "visualization", - }, - Object { - "id": "Apache-browsers-ecs", - "type": "visualization", - }, - Object { - "id": "Apache-response-codes-of-top-URLs-ecs", - "type": "visualization", - }, - Object { - "id": "Apache-access-logs-ecs", - "type": "search", - }, - Object { - "id": "Apache-errors-log-ecs", - "type": "search", - }, - Object { - "id": "Apache-HTTPD-ecs", - "type": "search", - }, - ], - "installed_kibana_space_id": "default", - "name": "apache", - "package_assets": Array [ - Object { - "id": "2f1ab9c0-8cf6-5e83-afcd-0d12851c8108", - "type": "epm-packages-assets", - }, - Object { - "id": "841166f1-6db0-5f7a-a8d9-768e88ddf984", - "type": "epm-packages-assets", - }, - Object { - "id": "b12ae5e1-daf2-51a7-99d8-0888d1f13b5b", - "type": "epm-packages-assets", - }, - Object { - "id": "2f263b24-c36a-5ea8-a707-76d1f274c888", - "type": "epm-packages-assets", - }, - Object { - "id": "bd5ff9ad-ba4a-5215-b5af-cef58a3aa886", - "type": "epm-packages-assets", - }, - Object { - "id": "5fc59aa9-1d7e-50ae-8ce5-b875ab44cfc5", - "type": "epm-packages-assets", - }, - Object { - "id": "7c850453-346b-5010-a946-28b83fc69e48", - "type": "epm-packages-assets", - }, - Object { - "id": "f02f8adb-3e0c-5f2f-b4f2-a04dc645b713", - "type": "epm-packages-assets", - }, - Object { - "id": "889d88db-6214-5836-aeff-1a87f8513b27", - "type": "epm-packages-assets", - }, - Object { - "id": "06a6b940-a745-563c-abf4-83eb3335926b", - "type": "epm-packages-assets", - }, - Object { - "id": "e68fd7ac-302e-5b75-bbbb-d69b441c8848", - "type": "epm-packages-assets", - }, - Object { - "id": "2c57fe0f-3b1a-57da-a63b-28f9b9e82bce", - "type": "epm-packages-assets", - }, - Object { - "id": "13db43e8-f8f9-57f0-b131-a171c2f2070f", - "type": "epm-packages-assets", - }, - Object { - "id": "e8750081-1c0b-5c55-bcab-fa6d47f01a85", - "type": "epm-packages-assets", - }, - Object { - "id": "71af57fe-25c4-5935-9879-ca4a2fba730e", - "type": "epm-packages-assets", - }, - Object { - "id": "cc287718-9573-5c56-a9ed-6dfef6589506", - "type": "epm-packages-assets", - }, - Object { - "id": "8badd8ba-289a-5e60-a1c0-f3d39e15cda3", - "type": "epm-packages-assets", - }, - Object { - "id": "20300efc-10eb-5fac-ba90-f6aa9b467e84", - "type": "epm-packages-assets", - }, - Object { - "id": "047c89df-33c2-5d74-b0a4-8b441879761c", - "type": "epm-packages-assets", - }, - Object { - "id": "9838a13f-1b89-5c54-844e-978620d66a1d", - "type": "epm-packages-assets", - }, - Object { - "id": "e105414b-221d-5433-8b24-452625f59b7c", - "type": "epm-packages-assets", - }, - Object { - "id": "eb166c25-843b-5271-8d43-6fb005d2df5a", - "type": "epm-packages-assets", - }, - Object { - "id": "342dbf4d-d88d-53e8-b365-d3639ebbbb14", - "type": "epm-packages-assets", - }, - Object { - "id": "f98c44a3-eaea-505f-8598-3b7f1097ef59", - "type": "epm-packages-assets", - }, - Object { - "id": "12da8c6c-d0e3-589c-9244-88d857ea76b6", - "type": "epm-packages-assets", - }, - Object { - "id": "e2d151ed-709c-542d-b797-cb95f353b9b3", - "type": "epm-packages-assets", - }, - Object { - "id": "f434cffe-0b00-59de-a17f-c1e71bd4ab0f", - "type": "epm-packages-assets", - }, - Object { - "id": "5bd0c25f-04a5-5fd0-8298-ba9aa2f6fe5e", - "type": "epm-packages-assets", - }, - Object { - "id": "279da3a3-8e9b-589b-86e0-bd7364821bab", - "type": "epm-packages-assets", - }, - Object { - "id": "b8758fcb-08bf-50fa-89bd-24398955298a", - "type": "epm-packages-assets", - }, - Object { - "id": "96e4eb36-03c3-5856-af44-559fd5133f2b", - "type": "epm-packages-assets", - }, - Object { - "id": "a59a79c3-66bd-5cfc-91f5-ee84f7227855", - "type": "epm-packages-assets", - }, - Object { - "id": "395143f9-54bf-5b46-b1be-a7b2a6142ad9", - "type": "epm-packages-assets", - }, - Object { - "id": "3449b8d2-ffd5-5aec-bb32-4245f2fbcde4", - "type": "epm-packages-assets", - }, - Object { - "id": "ab44094e-6c9d-50b8-b5c4-2e518d89912e", - "type": "epm-packages-assets", - }, - Object { - "id": "b093bfc0-6e98-5a1b-a502-e838a36f6568", - "type": "epm-packages-assets", - }, - Object { - "id": "03d86823-b756-5b91-850d-7ad231d33546", - "type": "epm-packages-assets", - }, - Object { - "id": "a76af2f0-049b-5be1-8d20-e87c9d1c2709", - "type": "epm-packages-assets", - }, - Object { - "id": "bc2f0c1e-992e-5407-9435-fedb39ff74ea", - "type": "epm-packages-assets", - }, - Object { - "id": "84668ac1-d5ef-545b-88f3-1e49f8f1c8ad", - "type": "epm-packages-assets", - }, - Object { - "id": "69b41271-91a0-5a2e-a62c-60364d5a9c8f", - "type": "epm-packages-assets", - }, - Object { - "id": "8e4ec555-5fbf-55d3-bea3-3af12c9aca3f", - "type": "epm-packages-assets", - }, - Object { - "id": "aa18f3f9-f62a-5ab8-9b34-75696efa5c48", - "type": "epm-packages-assets", - }, - Object { - "id": "71c8c6b1-2116-5817-b65f-7a87ef5ef2b7", - "type": "epm-packages-assets", - }, - Object { - "id": "8f6d7a1f-1e7f-5a60-8fe7-ce19115ed460", - "type": "epm-packages-assets", - }, - Object { - "id": "c115dbbf-edad-59f2-b046-c65a0373a81c", - "type": "epm-packages-assets", - }, - Object { - "id": "b7d696c3-8106-585c-9ecc-94a75cf1e3da", - "type": "epm-packages-assets", - }, - Object { - "id": "639e6a78-59d8-5ce8-9687-64e8f9af7e71", - "type": "epm-packages-assets", - }, - Object { - "id": "ae60c853-7a90-58d2-ab6c-04d3be5f1847", - "type": "epm-packages-assets", - }, - Object { - "id": "0cd33163-2ae4-57eb-96f6-c50af6685cab", - "type": "epm-packages-assets", - }, - Object { - "id": "39e0f78f-1172-5e61-9446-65ef3c0cb46c", - "type": "epm-packages-assets", - }, - Object { - "id": "b08f10ee-6afd-5e89-b9b4-569064fbdd9f", - "type": "epm-packages-assets", - }, - Object { - "id": "efcbe1c6-b2d5-521c-b27a-2146f08a604d", - "type": "epm-packages-assets", - }, - Object { - "id": "f9422c02-d43f-5ebb-b7c5-9e32f9b77c21", - "type": "epm-packages-assets", - }, - Object { - "id": "c276e880-3ba8-58e7-a5d5-c07707dba6b7", - "type": "epm-packages-assets", - }, - Object { - "id": "561a3711-c386-541c-9a77-2d0fa256caf6", - "type": "epm-packages-assets", - }, - Object { - "id": "1378350d-2e2b-52dd-ab3a-d8b9a09df92f", - "type": "epm-packages-assets", - }, - Object { - "id": "94e40729-4aea-59c8-86ba-075137c000dc", - "type": "epm-packages-assets", - }, - ], - "removable": true, - "version": "0.1.4", - }, - "id": "apache", - "namespaces": Array [], - "references": Array [], - "type": "epm-packages", - }, - "screenshots": Array [ - Object { - "path": "/package/apache/0.1.4/img/kibana-apache.png", - "size": "1215x1199", - "src": "/img/kibana-apache.png", - "title": "Apache Integration", - "type": "image/png", - }, - Object { - "path": "/package/apache/0.1.4/img/apache_httpd_server_status.png", - "size": "1919x1079", - "src": "/img/apache_httpd_server_status.png", - "title": "Apache HTTPD Server Status", - "type": "image/png", - }, - ], - "status": "installed", - "title": "Apache", - "type": "integration", - "version": "0.1.4", -} -`; diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_by_upload.ts b/x-pack/test/fleet_api_integration/apis/epm/install_by_upload.ts index 68cac70e8fed8..28b68609ce15e 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/install_by_upload.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/install_by_upload.ts @@ -75,7 +75,7 @@ export default function (providerContext: FtrProviderContext) { .type('application/gzip') .send(buf) .expect(200); - expect(res.body.items.length).to.be(32); + expect(res.body.items.length).to.be(29); }); it('should install a zip archive correctly and package info should return correctly after validation', async function () { @@ -86,7 +86,7 @@ export default function (providerContext: FtrProviderContext) { .type('application/zip') .send(buf) .expect(200); - expect(res.body.items.length).to.be(32); + expect(res.body.items.length).to.be(29); }); it('should throw an error if the archive is zip but content type is gzip', async function () { diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_overrides.ts b/x-pack/test/fleet_api_integration/apis/epm/install_overrides.ts index eee9525a4f062..834de2432d7c8 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/install_overrides.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/install_overrides.ts @@ -52,8 +52,7 @@ export default function (providerContext: FtrProviderContext) { // the index template composed_of has the correct component templates in the correct order const indexTemplate = indexTemplateResponse.index_templates[0].index_template; expect(indexTemplate.composed_of).to.eql([ - `${templateName}@mappings`, - `${templateName}@settings`, + `${templateName}@package`, `${templateName}@custom`, '.fleet_globals-1', '.fleet_agent_id_verification-1', @@ -62,25 +61,17 @@ export default function (providerContext: FtrProviderContext) { ({ body } = await es.transport.request( { method: 'GET', - path: `/_component_template/${templateName}@mappings`, + path: `/_component_template/${templateName}@package`, }, { meta: true, } )); - // The mappings override provided in the package is set in the mappings component template + // The mappings override provided in the package is set in the package component template expect(body.component_templates[0].component_template.template.mappings.dynamic).to.be(false); - ({ body } = await es.transport.request( - { - method: 'GET', - path: `/_component_template/${templateName}@settings`, - }, - { meta: true } - )); - - // The settings override provided in the package is set in the settings component template + // The settings override provided in the package is set in the package component template expect( body.component_templates[0].component_template.template.settings.index.lifecycle.name ).to.be('reference'); @@ -122,11 +113,7 @@ export default function (providerContext: FtrProviderContext) { // body: indexTemplate, // I *think* this should work, but it doesn't body: { index_patterns: [`${templateName}-*`], - composed_of: [ - `${templateName}@mappings`, - `${templateName}@settings`, - `${templateName}@custom`, - ], + composed_of: [`${templateName}@package`, `${templateName}@custom`], }, }, { meta: true } diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts b/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts index 4212ca46fc3c9..34cebed5b5d5a 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts @@ -91,29 +91,17 @@ export default function (providerContext: FtrProviderContext) { expect(resMetricsTemplate.statusCode).equal(404); }); it('should have uninstalled the component templates', async function () { - const resMappings = await es.transport.request( + const resPackage = await es.transport.request( { method: 'GET', - path: `/_component_template/${logsTemplateName}@mappings`, + path: `/_component_template/${logsTemplateName}@package`, }, { ignore: [404], meta: true, } ); - expect(resMappings.statusCode).equal(404); - - const resSettings = await es.transport.request( - { - method: 'GET', - path: `/_component_template/${logsTemplateName}@settings`, - }, - { - ignore: [404], - meta: true, - } - ); - expect(resSettings.statusCode).equal(404); + expect(resPackage.statusCode).equal(404); const resUserSettings = await es.transport.request( { @@ -382,22 +370,15 @@ const expectAssetsInstalled = ({ expect(res.statusCode).equal(200); }); it('should have installed the component templates', async function () { - const resMappings = await es.transport.request( + const resPackage = await es.transport.request( { method: 'GET', - path: `/_component_template/${logsTemplateName}@mappings`, + path: `/_component_template/${logsTemplateName}@package`, }, { meta: true } ); - expect(resMappings.statusCode).equal(200); - const resSettings = await es.transport.request( - { - method: 'GET', - path: `/_component_template/${logsTemplateName}@settings`, - }, - { meta: true } - ); - expect(resSettings.statusCode).equal(200); + expect(resPackage.statusCode).equal(200); + const resUserSettings = await es.transport.request( { method: 'GET', @@ -565,11 +546,7 @@ const expectAssetsInstalled = ({ installed_kibana_space_id: 'default', installed_es: [ { - id: 'logs-all_assets.test_logs@mappings', - type: 'component_template', - }, - { - id: 'logs-all_assets.test_logs@settings', + id: 'logs-all_assets.test_logs@package', type: 'component_template', }, { @@ -577,11 +554,7 @@ const expectAssetsInstalled = ({ type: 'component_template', }, { - id: 'metrics-all_assets.test_metrics@mappings', - type: 'component_template', - }, - { - id: 'metrics-all_assets.test_metrics@settings', + id: 'metrics-all_assets.test_metrics@package', type: 'component_template', }, { diff --git a/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts b/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts index 7a69d5635f9ac..b73ca9537990c 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts @@ -122,16 +122,16 @@ export default function (providerContext: FtrProviderContext) { }); }); it('should have populated the new component template with the correct mapping', async () => { - const resMappings = await es.transport.request( + const resPackage = await es.transport.request( { method: 'GET', - path: `/_component_template/${logsTemplateName2}@mappings`, + path: `/_component_template/${logsTemplateName2}@package`, }, { meta: true } ); - expect(resMappings.statusCode).equal(200); + expect(resPackage.statusCode).equal(200); expect( - resMappings.body.component_templates[0].component_template.template.mappings.properties + resPackage.body.component_templates[0].component_template.template.mappings.properties ).eql({ '@timestamp': { type: 'date', @@ -204,24 +204,31 @@ export default function (providerContext: FtrProviderContext) { expect(resPipeline2.statusCode).equal(404); }); it('should have updated the logs component templates', async function () { - const resMappings = await es.transport.request( + const resPackage = await es.transport.request( { method: 'GET', - path: `/_component_template/${logsTemplateName}@mappings`, + path: `/_component_template/${logsTemplateName}@package`, }, { meta: true } ); - expect(resMappings.statusCode).equal(200); - expect(resMappings.body.component_templates[0].component_template.template.settings).eql({ + expect(resPackage.statusCode).equal(200); + expect(resPackage.body.component_templates[0].component_template.template.settings).eql({ index: { + codec: 'best_compression', + lifecycle: { + name: 'reference2', + }, mapping: { total_fields: { limit: '10000', }, }, + query: { + default_field: ['logs_test_name', 'new_field_name'], + }, }, }); - expect(resMappings.body.component_templates[0].component_template.template.mappings).eql({ + expect(resPackage.body.component_templates[0].component_template.template.mappings).eql({ dynamic: true, properties: { '@timestamp': { @@ -249,23 +256,7 @@ export default function (providerContext: FtrProviderContext) { }, }, }); - const resSettings = await es.transport.request( - { - method: 'GET', - path: `/_component_template/${logsTemplateName}@settings`, - }, - { meta: true } - ); - expect(resSettings.statusCode).equal(200); - expect(resSettings.body.component_templates[0].component_template.template.settings).eql({ - index: { - lifecycle: { name: 'reference2' }, - codec: 'best_compression', - query: { - default_field: ['logs_test_name', 'new_field_name'], - }, - }, - }); + const resUserSettings = await es.transport.request( { method: 'GET', @@ -295,16 +286,16 @@ export default function (providerContext: FtrProviderContext) { }); }); it('should have updated the metrics mapping component template', async function () { - const resMappings = await es.transport.request( + const resPackage = await es.transport.request( { method: 'GET', - path: `/_component_template/${metricsTemplateName}@mappings`, + path: `/_component_template/${metricsTemplateName}@package`, }, { meta: true } ); - expect(resMappings.statusCode).equal(200); + expect(resPackage.statusCode).equal(200); expect( - resMappings.body.component_templates[0].component_template.template.mappings.properties + resPackage.body.component_templates[0].component_template.template.mappings.properties ).eql({ '@timestamp': { type: 'date', @@ -432,11 +423,7 @@ export default function (providerContext: FtrProviderContext) { type: 'index_template', }, { - id: 'logs-all_assets.test_logs@mappings', - type: 'component_template', - }, - { - id: 'logs-all_assets.test_logs@settings', + id: 'logs-all_assets.test_logs@package', type: 'component_template', }, { @@ -448,11 +435,7 @@ export default function (providerContext: FtrProviderContext) { type: 'index_template', }, { - id: 'logs-all_assets.test_logs2@mappings', - type: 'component_template', - }, - { - id: 'logs-all_assets.test_logs2@settings', + id: 'logs-all_assets.test_logs2@package', type: 'component_template', }, { @@ -464,11 +447,7 @@ export default function (providerContext: FtrProviderContext) { type: 'index_template', }, { - id: 'metrics-all_assets.test_metrics@mappings', - type: 'component_template', - }, - { - id: 'metrics-all_assets.test_metrics@settings', + id: 'metrics-all_assets.test_metrics@package', type: 'component_template', }, { From 2e4274ca88c86a6a49934602f1ebb29913397ab0 Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Tue, 29 Mar 2022 11:10:35 -0400 Subject: [PATCH 28/37] [CI] Move hourly CI tasks to on merge (#128505) --- .buildkite/pipelines/hourly.yml | 218 -------------- .buildkite/pipelines/on_merge.yml | 216 +++++++++++++- .buildkite/pipelines/pull_request/base.yml | 7 + .../scripts/lifecycle/ci_stats_ready.js | 23 ++ .buildkite/scripts/steps/build_api_docs.sh | 8 + .buildkite/scripts/steps/checks.sh | 2 +- .../scripts/steps/checks/check_types.sh | 9 + .../type_check_plugin_public_api_docs.sh | 40 --- .buildkite/scripts/steps/ci_stats_ready.sh | 7 + .../steps/on_merge_build_and_metrics.sh | 2 + .buildkite/yarn.lock | 282 ++++++++---------- 11 files changed, 388 insertions(+), 426 deletions(-) delete mode 100644 .buildkite/pipelines/hourly.yml create mode 100644 .buildkite/scripts/lifecycle/ci_stats_ready.js create mode 100755 .buildkite/scripts/steps/build_api_docs.sh create mode 100755 .buildkite/scripts/steps/checks/check_types.sh delete mode 100755 .buildkite/scripts/steps/checks/type_check_plugin_public_api_docs.sh create mode 100755 .buildkite/scripts/steps/ci_stats_ready.sh diff --git a/.buildkite/pipelines/hourly.yml b/.buildkite/pipelines/hourly.yml deleted file mode 100644 index 1335866675564..0000000000000 --- a/.buildkite/pipelines/hourly.yml +++ /dev/null @@ -1,218 +0,0 @@ -steps: - - command: .buildkite/scripts/lifecycle/pre_build.sh - label: Pre-Build - timeout_in_minutes: 10 - agents: - queue: kibana-default - - - wait - - - command: .buildkite/scripts/steps/build_kibana.sh - label: Build Kibana Distribution and Plugins - agents: - queue: c2-16 - key: build - if: "build.env('KIBANA_BUILD_ID') == null || build.env('KIBANA_BUILD_ID') == ''" - timeout_in_minutes: 60 - - - command: .buildkite/scripts/steps/functional/xpack_cigroup.sh - label: 'Default CI Group' - parallelism: 27 - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 250 - key: default-cigroup - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: CI_GROUP=Docker .buildkite/scripts/steps/functional/xpack_cigroup.sh - label: 'Docker CI Group' - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 - key: default-cigroup-docker - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/oss_cigroup.sh - label: 'OSS CI Group' - parallelism: 11 - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 - key: oss-cigroup - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/oss_accessibility.sh - label: 'OSS Accessibility Tests' - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/xpack_accessibility.sh - label: 'Default Accessibility Tests' - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/oss_firefox.sh - label: 'OSS Firefox Tests' - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/xpack_firefox.sh - label: 'Default Firefox Tests' - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/oss_misc.sh - label: 'OSS Misc Functional Tests' - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/xpack_saved_object_field_metrics.sh - label: 'Saved Object Field Metrics' - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/test/jest.sh - label: 'Jest Tests' - parallelism: 8 - agents: - queue: n2-4-spot - timeout_in_minutes: 90 - key: jest - retry: - automatic: - - exit_status: '-1' - limit: 3 - - - command: .buildkite/scripts/steps/test/jest_integration.sh - label: 'Jest Integration Tests' - parallelism: 3 - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - key: jest-integration - retry: - automatic: - - exit_status: '-1' - limit: 3 - - - command: .buildkite/scripts/steps/test/api_integration.sh - label: 'API Integration Tests' - agents: - queue: n2-2-spot - timeout_in_minutes: 120 - key: api-integration - retry: - automatic: - - exit_status: '-1' - limit: 3 - - - command: .buildkite/scripts/steps/lint.sh - label: 'Linting' - agents: - queue: n2-2-spot - key: linting - timeout_in_minutes: 90 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - - command: .buildkite/scripts/steps/lint_with_types.sh - label: 'Linting (with types)' - agents: - queue: c2-16 - key: linting_with_types - timeout_in_minutes: 90 - - - command: .buildkite/scripts/steps/checks.sh - label: 'Checks' - agents: - queue: c2-8 - key: checks - timeout_in_minutes: 120 - - - command: .buildkite/scripts/steps/storybooks/build_and_upload.sh - label: 'Build Storybooks' - agents: - queue: n2-4-spot - key: storybooks - timeout_in_minutes: 60 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - - wait: ~ - continue_on_failure: true - - - command: .buildkite/scripts/lifecycle/post_build.sh - label: Post-Build - timeout_in_minutes: 10 - agents: - queue: kibana-default diff --git a/.buildkite/pipelines/on_merge.yml b/.buildkite/pipelines/on_merge.yml index c6acb48b3e212..a11f566b06093 100644 --- a/.buildkite/pipelines/on_merge.yml +++ b/.buildkite/pipelines/on_merge.yml @@ -7,13 +7,18 @@ steps: timeout_in_minutes: 10 agents: queue: kibana-default + retry: + automatic: + - exit_status: '*' + limit: 1 - wait - command: .buildkite/scripts/steps/on_merge_build_and_metrics.sh - label: Default Build and Metrics + label: Build Kibana Distribution and Plugins agents: - queue: c2-8 + queue: c2-16 + key: build timeout_in_minutes: 60 retry: automatic: @@ -22,6 +27,7 @@ steps: - command: .buildkite/scripts/steps/on_merge_ts_refs_api_docs.sh label: Build TS Refs and Check Public API Docs + key: public-api-docs agents: queue: c2-4 timeout_in_minutes: 80 @@ -30,6 +36,212 @@ steps: - exit_status: '*' limit: 1 + - command: .buildkite/scripts/steps/ci_stats_ready.sh + label: Mark CI Stats as ready + agents: + queue: kibana-default + timeout_in_minutes: 10 + depends_on: + - build + - public-api-docs + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/xpack_cigroup.sh + label: 'Default CI Group' + parallelism: 27 + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 250 + key: default-cigroup + retry: + automatic: + - exit_status: '-1' + limit: 3 + - exit_status: '*' + limit: 1 + + - command: CI_GROUP=Docker .buildkite/scripts/steps/functional/xpack_cigroup.sh + label: 'Docker CI Group' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 120 + key: default-cigroup-docker + retry: + automatic: + - exit_status: '-1' + limit: 3 + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/oss_cigroup.sh + label: 'OSS CI Group' + parallelism: 11 + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 120 + key: oss-cigroup + retry: + automatic: + - exit_status: '-1' + limit: 3 + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/oss_accessibility.sh + label: 'OSS Accessibility Tests' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '-1' + limit: 3 + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/xpack_accessibility.sh + label: 'Default Accessibility Tests' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '-1' + limit: 3 + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/oss_firefox.sh + label: 'OSS Firefox Tests' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '-1' + limit: 3 + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/xpack_firefox.sh + label: 'Default Firefox Tests' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '-1' + limit: 3 + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/oss_misc.sh + label: 'OSS Misc Functional Tests' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '-1' + limit: 3 + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/xpack_saved_object_field_metrics.sh + label: 'Saved Object Field Metrics' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '-1' + limit: 3 + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/test/jest.sh + label: 'Jest Tests' + parallelism: 8 + agents: + queue: n2-4-spot + timeout_in_minutes: 90 + key: jest + retry: + automatic: + - exit_status: '-1' + limit: 3 + + - command: .buildkite/scripts/steps/test/jest_integration.sh + label: 'Jest Integration Tests' + parallelism: 3 + agents: + queue: n2-4-spot + timeout_in_minutes: 120 + key: jest-integration + retry: + automatic: + - exit_status: '-1' + limit: 3 + + - command: .buildkite/scripts/steps/test/api_integration.sh + label: 'API Integration Tests' + agents: + queue: n2-2-spot + timeout_in_minutes: 120 + key: api-integration + retry: + automatic: + - exit_status: '-1' + limit: 3 + + - command: .buildkite/scripts/steps/lint.sh + label: 'Linting' + agents: + queue: n2-2-spot + key: linting + timeout_in_minutes: 90 + retry: + automatic: + - exit_status: '-1' + limit: 3 + + - command: .buildkite/scripts/steps/lint_with_types.sh + label: 'Linting (with types)' + agents: + queue: c2-16 + key: linting_with_types + timeout_in_minutes: 90 + + - command: .buildkite/scripts/steps/checks.sh + label: 'Checks' + agents: + queue: c2-8 + key: checks + timeout_in_minutes: 120 + + - command: .buildkite/scripts/steps/storybooks/build_and_upload.sh + label: 'Build Storybooks' + agents: + queue: n2-4-spot + key: storybooks + timeout_in_minutes: 60 + retry: + automatic: + - exit_status: '-1' + limit: 3 + - wait: ~ continue_on_failure: true diff --git a/.buildkite/pipelines/pull_request/base.yml b/.buildkite/pipelines/pull_request/base.yml index 4aa9a7a49fe23..eaad8ca875545 100644 --- a/.buildkite/pipelines/pull_request/base.yml +++ b/.buildkite/pipelines/pull_request/base.yml @@ -170,6 +170,13 @@ steps: key: storybooks timeout_in_minutes: 60 + - command: .buildkite/scripts/steps/build_api_docs.sh + label: 'Build API Docs' + agents: + queue: n2-4 + key: build_api_docs + timeout_in_minutes: 60 + - command: .buildkite/scripts/steps/webpack_bundle_analyzer/build_and_upload.sh label: 'Build Webpack Bundle Analyzer reports' agents: diff --git a/.buildkite/scripts/lifecycle/ci_stats_ready.js b/.buildkite/scripts/lifecycle/ci_stats_ready.js new file mode 100644 index 0000000000000..98b03a56d0023 --- /dev/null +++ b/.buildkite/scripts/lifecycle/ci_stats_ready.js @@ -0,0 +1,23 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +// eslint-disable-next-line import/no-unresolved +const { CiStats } = require('kibana-buildkite-library'); + +(async () => { + try { + await CiStats.onMetricsViable(); + } catch (ex) { + console.error('CI Stats Error', ex.message); + if (ex.response) { + console.error('HTTP Error Response Status', ex.response.status); + console.error('HTTP Error Response Body', ex.response.data); + } + process.exit(1); + } +})(); diff --git a/.buildkite/scripts/steps/build_api_docs.sh b/.buildkite/scripts/steps/build_api_docs.sh new file mode 100755 index 0000000000000..2f63d6efa0941 --- /dev/null +++ b/.buildkite/scripts/steps/build_api_docs.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -euo pipefail + +.buildkite/scripts/bootstrap.sh + +echo "--- Build API Docs" +node --max-old-space-size=12000 scripts/build_api_docs diff --git a/.buildkite/scripts/steps/checks.sh b/.buildkite/scripts/steps/checks.sh index bde9e82a4d1e6..cae019150b626 100755 --- a/.buildkite/scripts/steps/checks.sh +++ b/.buildkite/scripts/steps/checks.sh @@ -13,7 +13,7 @@ export DISABLE_BOOTSTRAP_VALIDATION=false .buildkite/scripts/steps/checks/doc_api_changes.sh .buildkite/scripts/steps/checks/kbn_pm_dist.sh .buildkite/scripts/steps/checks/plugin_list_docs.sh -.buildkite/scripts/steps/checks/type_check_plugin_public_api_docs.sh +.buildkite/scripts/steps/checks/check_types.sh .buildkite/scripts/steps/checks/bundle_limits.sh .buildkite/scripts/steps/checks/i18n.sh .buildkite/scripts/steps/checks/file_casing.sh diff --git a/.buildkite/scripts/steps/checks/check_types.sh b/.buildkite/scripts/steps/checks/check_types.sh new file mode 100755 index 0000000000000..3b649a73e8060 --- /dev/null +++ b/.buildkite/scripts/steps/checks/check_types.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source .buildkite/scripts/common/util.sh + +echo --- Check Types +checks-reporter-with-killswitch "Check Types" \ + node scripts/type_check diff --git a/.buildkite/scripts/steps/checks/type_check_plugin_public_api_docs.sh b/.buildkite/scripts/steps/checks/type_check_plugin_public_api_docs.sh deleted file mode 100755 index e2a4bd9d9d427..0000000000000 --- a/.buildkite/scripts/steps/checks/type_check_plugin_public_api_docs.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source .buildkite/scripts/common/util.sh - -echo --- Build TS Refs -checks-reporter-with-killswitch "Build TS Refs" \ - node scripts/build_ts_refs \ - --clean \ - --no-cache \ - --force - -set +e; -echo "--- running check types and build api docs in parallel"; - -checks-reporter-with-killswitch "Check Types" \ - node scripts/type_check &> target/check_types.log & -check_types_pid=$! - -node --max-old-space-size=12000 scripts/build_api_docs &> target/build_api_docs.log & -api_docs_pid=$! - -wait $check_types_pid -check_types_exit=$? - -wait $api_docs_pid -api_docs_exit=$? - -echo --- Check Types -cat target/check_types.log -if [[ "$check_types_exit" != "0" ]]; then echo "^^^ +++"; fi - -echo --- Building api docs -cat target/build_api_docs.log -if [[ "$api_docs_exit" != "0" ]]; then echo "^^^ +++"; fi - -if [[ "${api_docs_exit}${check_types_exit}" != "00" ]]; then - exit 1 -fi diff --git a/.buildkite/scripts/steps/ci_stats_ready.sh b/.buildkite/scripts/steps/ci_stats_ready.sh new file mode 100755 index 0000000000000..92ea6a70fd779 --- /dev/null +++ b/.buildkite/scripts/steps/ci_stats_ready.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source .buildkite/scripts/common/util.sh + +node .buildkite/scripts/lifecycle/ci_stats_ready.js diff --git a/.buildkite/scripts/steps/on_merge_build_and_metrics.sh b/.buildkite/scripts/steps/on_merge_build_and_metrics.sh index 1f1e492f87bec..fb05bb99b0c54 100755 --- a/.buildkite/scripts/steps/on_merge_build_and_metrics.sh +++ b/.buildkite/scripts/steps/on_merge_build_and_metrics.sh @@ -4,5 +4,7 @@ set -euo pipefail .buildkite/scripts/bootstrap.sh .buildkite/scripts/build_kibana.sh +.buildkite/scripts/build_kibana_plugins.sh +.buildkite/scripts/post_build_kibana_plugins.sh .buildkite/scripts/post_build_kibana.sh .buildkite/scripts/saved_object_field_metrics.sh diff --git a/.buildkite/yarn.lock b/.buildkite/yarn.lock index 2c3ce924b22ea..c2d6928d30c5a 100644 --- a/.buildkite/yarn.lock +++ b/.buildkite/yarn.lock @@ -3,226 +3,178 @@ "@octokit/auth-token@^2.4.4": - version "2.4.4" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.4.tgz#ee31c69b01d0378c12fd3ffe406030f3d94d3b56" - integrity sha512-LNfGu3Ro9uFAYh10MUZVaT7X2CnNm2C8IDQmabx+3DygYIQjs9FwzFAHN/0t6mu5HEPhxcb1XOuxdpY82vCg2Q== + "integrity" "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==" + "resolved" "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz" + "version" "2.5.0" dependencies: - "@octokit/types" "^6.0.0" + "@octokit/types" "^6.0.3" -"@octokit/core@^3.5.1": - version "3.5.1" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.5.1.tgz#8601ceeb1ec0e1b1b8217b960a413ed8e947809b" - integrity sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw== +"@octokit/core@^3.5.1", "@octokit/core@>=2", "@octokit/core@>=3": + "integrity" "sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw==" + "resolved" "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz" + "version" "3.5.1" dependencies: "@octokit/auth-token" "^2.4.4" "@octokit/graphql" "^4.5.8" "@octokit/request" "^5.6.0" "@octokit/request-error" "^2.0.5" "@octokit/types" "^6.0.3" - before-after-hook "^2.2.0" - universal-user-agent "^6.0.0" + "before-after-hook" "^2.2.0" + "universal-user-agent" "^6.0.0" "@octokit/endpoint@^6.0.1": - version "6.0.9" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.9.tgz#c6a772e024202b1bd19ab69f90e0536a2598b13e" - integrity sha512-3VPLbcCuqji4IFTclNUtGdp9v7g+nspWdiCUbK3+iPMjJCZ6LEhn1ts626bWLOn0GiDb6j+uqGvPpqLnY7pBgw== + "integrity" "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==" + "resolved" "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz" + "version" "6.0.12" dependencies: - "@octokit/types" "^5.0.0" - is-plain-object "^5.0.0" - universal-user-agent "^6.0.0" + "@octokit/types" "^6.0.3" + "is-plain-object" "^5.0.0" + "universal-user-agent" "^6.0.0" "@octokit/graphql@^4.5.8": - version "4.5.8" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.5.8.tgz#d42373633c3015d0eafce64a8ce196be167fdd9b" - integrity sha512-WnCtNXWOrupfPJgXe+vSmprZJUr0VIu14G58PMlkWGj3cH+KLZEfKMmbUQ6C3Wwx6fdhzVW1CD5RTnBdUHxhhA== + "integrity" "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==" + "resolved" "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz" + "version" "4.8.0" dependencies: - "@octokit/request" "^5.3.0" - "@octokit/types" "^6.0.0" - universal-user-agent "^6.0.0" + "@octokit/request" "^5.6.0" + "@octokit/types" "^6.0.3" + "universal-user-agent" "^6.0.0" "@octokit/openapi-types@^11.2.0": - version "11.2.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-11.2.0.tgz#b38d7fc3736d52a1e96b230c1ccd4a58a2f400a6" - integrity sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA== - -"@octokit/openapi-types@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-2.2.0.tgz#123e0438a0bc718ccdac3b5a2e69b3dd00daa85b" - integrity sha512-274lNUDonw10kT8wHg8fCcUc1ZjZHbWv0/TbAwb0ojhBQqZYc1cQ/4yqTVTtPMDeZ//g7xVEYe/s3vURkRghPg== + "integrity" "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==" + "resolved" "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz" + "version" "11.2.0" "@octokit/plugin-paginate-rest@^2.16.8": - version "2.17.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz#32e9c7cab2a374421d3d0de239102287d791bce7" - integrity sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw== + "integrity" "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==" + "resolved" "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz" + "version" "2.17.0" dependencies: "@octokit/types" "^6.34.0" "@octokit/plugin-request-log@^1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" - integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== + "integrity" "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==" + "resolved" "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz" + "version" "1.0.4" "@octokit/plugin-rest-endpoint-methods@^5.12.0": - version "5.13.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz#8c46109021a3412233f6f50d28786f8e552427ba" - integrity sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA== + "integrity" "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==" + "resolved" "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz" + "version" "5.13.0" dependencies: "@octokit/types" "^6.34.0" - deprecation "^2.3.1" - -"@octokit/request-error@^2.0.0": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.0.3.tgz#b51b200052bf483f6fa56c9e7e3aa51ead36ecd8" - integrity sha512-GgD5z8Btm301i2zfvJLk/mkhvGCdjQ7wT8xF9ov5noQY8WbKZDH9cOBqXzoeKd1mLr1xH2FwbtGso135zGBgTA== - dependencies: - "@octokit/types" "^5.0.1" - deprecation "^2.0.0" - once "^1.4.0" + "deprecation" "^2.3.1" "@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677" - integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== + "integrity" "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==" + "resolved" "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz" + "version" "2.1.0" dependencies: "@octokit/types" "^6.0.3" - deprecation "^2.0.0" - once "^1.4.0" - -"@octokit/request@^5.3.0": - version "5.4.12" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.12.tgz#b04826fa934670c56b135a81447be2c1723a2ffc" - integrity sha512-MvWYdxengUWTGFpfpefBBpVmmEYfkwMoxonIB3sUGp5rhdgwjXL1ejo6JbgzG/QD9B/NYt/9cJX1pxXeSIUCkg== - dependencies: - "@octokit/endpoint" "^6.0.1" - "@octokit/request-error" "^2.0.0" - "@octokit/types" "^6.0.3" - deprecation "^2.0.0" - is-plain-object "^5.0.0" - node-fetch "^2.6.1" - once "^1.4.0" - universal-user-agent "^6.0.0" + "deprecation" "^2.0.0" + "once" "^1.4.0" "@octokit/request@^5.6.0": - version "5.6.2" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.2.tgz#1aa74d5da7b9e04ac60ef232edd9a7438dcf32d8" - integrity sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA== + "integrity" "sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA==" + "resolved" "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz" + "version" "5.6.2" dependencies: "@octokit/endpoint" "^6.0.1" "@octokit/request-error" "^2.1.0" "@octokit/types" "^6.16.1" - is-plain-object "^5.0.0" - node-fetch "^2.6.1" - universal-user-agent "^6.0.0" + "is-plain-object" "^5.0.0" + "node-fetch" "^2.6.1" + "universal-user-agent" "^6.0.0" "@octokit/rest@^18.10.0": - version "18.12.0" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.12.0.tgz#f06bc4952fc87130308d810ca9d00e79f6988881" - integrity sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q== + "integrity" "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==" + "resolved" "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz" + "version" "18.12.0" dependencies: "@octokit/core" "^3.5.1" "@octokit/plugin-paginate-rest" "^2.16.8" "@octokit/plugin-request-log" "^1.0.4" "@octokit/plugin-rest-endpoint-methods" "^5.12.0" -"@octokit/types@^5.0.0", "@octokit/types@^5.0.1": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-5.5.0.tgz#e5f06e8db21246ca102aa28444cdb13ae17a139b" - integrity sha512-UZ1pErDue6bZNjYOotCNveTXArOMZQFG6hKJfOnGnulVCMcVVi7YIIuuR4WfBhjo7zgpmzn/BkPDnUXtNx+PcQ== +"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.34.0": + "integrity" "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==" + "resolved" "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz" + "version" "6.34.0" dependencies: - "@types/node" ">= 8" + "@octokit/openapi-types" "^11.2.0" -"@octokit/types@^6.0.0", "@octokit/types@^6.0.3": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.2.1.tgz#7f881fe44475ab1825776a4a59ca1ae082ed1043" - integrity sha512-jHs9OECOiZxuEzxMZcXmqrEO8GYraHF+UzNVH2ACYh8e/Y7YoT+hUf9ldvVd6zIvWv4p3NdxbQ0xx3ku5BnSiA== +"axios@^0.21.4": + "integrity" "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==" + "resolved" "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz" + "version" "0.21.4" dependencies: - "@octokit/openapi-types" "^2.2.0" - "@types/node" ">= 8" + "follow-redirects" "^1.14.0" -"@octokit/types@^6.16.1", "@octokit/types@^6.34.0": - version "6.34.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.34.0.tgz#c6021333334d1ecfb5d370a8798162ddf1ae8218" - integrity sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw== - dependencies: - "@octokit/openapi-types" "^11.2.0" +"before-after-hook@^2.2.0": + "integrity" "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==" + "resolved" "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz" + "version" "2.2.2" -"@types/node@>= 8": - version "16.10.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.2.tgz#5764ca9aa94470adb4e1185fe2e9f19458992b2e" - integrity sha512-zCclL4/rx+W5SQTzFs9wyvvyCwoK9QtBpratqz2IYJ3O8Umrn0m3nsTv0wQBk9sRGpvUe9CwPDrQFB10f1FIjQ== +"deprecation@^2.0.0", "deprecation@^2.3.1": + "integrity" "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + "resolved" "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz" + "version" "2.3.1" -axios@^0.21.4: - version "0.21.4" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" - integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== - dependencies: - follow-redirects "^1.14.0" - -before-after-hook@^2.2.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" - integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== - -deprecation@^2.0.0, deprecation@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" - integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== - -follow-redirects@^1.14.0: - version "1.14.3" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.3.tgz#6ada78118d8d24caee595595accdc0ac6abd022e" - integrity sha512-3MkHxknWMUtb23apkgz/83fDoe+y+qr0TdgacGIA7bew+QLBo3vdgEN2xEsuXNivpFy4CyDhBBZnNZOtalmenw== - -is-plain-object@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" - integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== - -kibana-buildkite-library@elastic/kibana-buildkite-library: - version "1.0.0" - resolved "https://codeload.github.com/elastic/kibana-buildkite-library/tar.gz/f67122968ea54ba14036b55c9f99906d96a3733d" +"follow-redirects@^1.14.0": + "integrity" "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==" + "resolved" "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz" + "version" "1.14.5" + +"is-plain-object@^5.0.0": + "integrity" "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + "resolved" "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz" + "version" "5.0.0" + +"kibana-buildkite-library@github:elastic/kibana-buildkite-library": + "resolved" "git+ssh://git@github.com/elastic/kibana-buildkite-library.git#ccf5b824c4294d1fdf3569d32218d3bdb0958121" + "version" "1.0.0" dependencies: "@octokit/rest" "^18.10.0" - axios "^0.21.4" + "axios" "^0.21.4" -node-fetch@^2.6.1: - version "2.6.5" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.5.tgz#42735537d7f080a7e5f78b6c549b7146be1742fd" - integrity sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ== +"node-fetch@^2.6.1": + "integrity" "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==" + "resolved" "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz" + "version" "2.6.6" dependencies: - whatwg-url "^5.0.0" + "whatwg-url" "^5.0.0" -once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= +"once@^1.4.0": + "integrity" "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=" + "resolved" "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + "version" "1.4.0" dependencies: - wrappy "1" - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= - -universal-user-agent@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" - integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + "wrappy" "1" + +"tr46@~0.0.3": + "integrity" "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + "resolved" "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" + "version" "0.0.3" + +"universal-user-agent@^6.0.0": + "integrity" "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + "resolved" "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz" + "version" "6.0.0" + +"webidl-conversions@^3.0.0": + "integrity" "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + "resolved" "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" + "version" "3.0.1" + +"whatwg-url@^5.0.0": + "integrity" "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=" + "resolved" "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" + "version" "5.0.0" dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" + "tr46" "~0.0.3" + "webidl-conversions" "^3.0.0" -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +"wrappy@1": + "integrity" "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "resolved" "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "version" "1.0.2" From 437adb58805e3b524851de4b9727d7e3951c3f4c Mon Sep 17 00:00:00 2001 From: Mat Schaffer Date: Wed, 30 Mar 2022 00:13:35 +0900 Subject: [PATCH 29/37] [Stack Monitoring] Rename "APM & Fleet Server" to "Integrations Server" (#128574) --- .../dev_docs/how_to/running_components_from_source.md | 2 +- .../monitoring/public/components/apm/apm_metrics.tsx | 6 ++---- .../public/components/cluster/overview/apm_panel.js | 8 ++++---- x-pack/plugins/monitoring/public/lib/apm_agent.ts | 5 +++-- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/monitoring/dev_docs/how_to/running_components_from_source.md b/x-pack/plugins/monitoring/dev_docs/how_to/running_components_from_source.md index fcb3bae429e7c..2008d913238f5 100644 --- a/x-pack/plugins/monitoring/dev_docs/how_to/running_components_from_source.md +++ b/x-pack/plugins/monitoring/dev_docs/how_to/running_components_from_source.md @@ -323,7 +323,7 @@ And start it with: ./apm-server -c apm-server.source.yml -e -d "*" ``` -Note that on cloud the APM server section will show up as "APM & Fleet Server", but the code paths are the same. +Note that on cloud the APM server section will show up as "Integrations Server" (previously "APM & Fleet"), but the code paths are the same. ## Logstash diff --git a/x-pack/plugins/monitoring/public/components/apm/apm_metrics.tsx b/x-pack/plugins/monitoring/public/components/apm/apm_metrics.tsx index c4e5c51856013..2f6eea57b6090 100644 --- a/x-pack/plugins/monitoring/public/components/apm/apm_metrics.tsx +++ b/x-pack/plugins/monitoring/public/components/apm/apm_metrics.tsx @@ -22,8 +22,6 @@ import { FormattedMessage } from '@kbn/i18n-react'; // @ts-ignore could not find declaration file import { MonitoringTimeseriesContainer } from '../chart'; -// @ts-ignore could not find declaration file -import { Status } from './instance/status'; import { checkAgentTypeMetric } from '../../lib/apm_agent'; interface TitleType { @@ -63,12 +61,12 @@ const getHeading = (isFleetTypeMetric: boolean) => { const titles: TitleType = {}; if (isFleetTypeMetric) { titles.title = i18n.translate('xpack.monitoring.apm.metrics.topCharts.agentTitle', { - defaultMessage: 'APM & Fleet Server - Resource Usage', + defaultMessage: 'Integrations Server - Resource Usage', }); titles.heading = ( ); return titles; diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js index 2a2e6719ab268..24b1d508bb3c1 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js @@ -39,14 +39,14 @@ const getServerTitle = (isFleetTypeMetric, total) => { linkLabel.link = ( ); linkLabel.aria = i18n.translate( 'xpack.monitoring.cluster.overview.apmPanel.instancesAndFleetsTotalLinkAriaLabel', { - defaultMessage: 'APM and Fleet server instances: {apmsTotal}', + defaultMessage: 'Integrations server instances: {apmsTotal}', values: { apmsTotal }, } ); @@ -73,7 +73,7 @@ const getServerTitle = (isFleetTypeMetric, total) => { const getOverviewTitle = (isFleetTypeMetric) => { if (isFleetTypeMetric) { return i18n.translate('xpack.monitoring.cluster.overview.apmPanel.overviewFleetLinkLabel', { - defaultMessage: 'APM & Fleet server overview', + defaultMessage: 'Integrations server overview', }); } return i18n.translate('xpack.monitoring.cluster.overview.apmPanel.overviewLinkLabel', { @@ -84,7 +84,7 @@ const getOverviewTitle = (isFleetTypeMetric) => { const getHeadingTitle = (isFleetTypeMetric) => { if (isFleetTypeMetric) { return i18n.translate('xpack.monitoring.cluster.overview.apmPanel.apmFleetTitle', { - defaultMessage: 'APM & Fleet server', + defaultMessage: 'Integrations server', }); } return i18n.translate('xpack.monitoring.cluster.overview.apmPanel.apmTitle', { diff --git a/x-pack/plugins/monitoring/public/lib/apm_agent.ts b/x-pack/plugins/monitoring/public/lib/apm_agent.ts index 09419b7c91bf0..bf992cc560d1e 100644 --- a/x-pack/plugins/monitoring/public/lib/apm_agent.ts +++ b/x-pack/plugins/monitoring/public/lib/apm_agent.ts @@ -8,8 +8,9 @@ import { Legacy } from '../legacy_shims'; /** - * Possible temporary work arround to establish if APM might also be monitoring fleet: - * https://github.com/elastic/kibana/pull/95129/files#r604815886 + * Checks if on cloud and >= 7.13 + * In this configuration APM server should be running within elastic agent. + * See https://github.com/elastic/kibana/issues/97879 for details. */ export const checkAgentTypeMetric = (versions?: string[]) => { if (!Legacy.shims.isCloud || !versions) { From 5efb4b1cf62be06ce7a24361050f03208fc0bfb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Zolt=C3=A1n=20Szab=C3=B3?= Date: Tue, 29 Mar 2022 17:22:27 +0200 Subject: [PATCH 30/37] [DOCS] Adds GET case API docs (#128606) --- docs/api/cases.asciidoc | 4 +- docs/api/cases/cases-api-get-case.asciidoc | 100 +++++++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 docs/api/cases/cases-api-get-case.asciidoc diff --git a/docs/api/cases.asciidoc b/docs/api/cases.asciidoc index 5aa837d35676e..273f3a0b51cc2 100644 --- a/docs/api/cases.asciidoc +++ b/docs/api/cases.asciidoc @@ -14,7 +14,7 @@ these APIs: * <> * {security-guide}/cases-api-get-case-activity.html[Get all case activity] * {security-guide}/cases-api-get-all-case-comments.html[Get all case comments] -* {security-guide}/cases-api-get-case.html[Get case] +* <> * {security-guide}/cases-api-get-comment.html[Get comment] * {security-guide}/cases-get-connector.html[Get current connector] * {security-guide}/cases-api-get-reporters.html[Get reporters] @@ -34,5 +34,7 @@ include::cases/cases-api-delete-comments.asciidoc[leveloffset=+1] //FIND include::cases/cases-api-find-cases.asciidoc[leveloffset=+1] include::cases/cases-api-find-connectors.asciidoc[leveloffset=+1] +//GET +include::cases/cases-api-get-case.asciidoc[leveloffset=+1] //UPDATE include::cases/cases-api-update.asciidoc[leveloffset=+1] diff --git a/docs/api/cases/cases-api-get-case.asciidoc b/docs/api/cases/cases-api-get-case.asciidoc new file mode 100644 index 0000000000000..6bd255f6f8326 --- /dev/null +++ b/docs/api/cases/cases-api-get-case.asciidoc @@ -0,0 +1,100 @@ +[[cases-api-get-case]] +== Get case API +++++ +Get case +++++ + +Returns a specified case. + +=== Request + +`GET :/api/cases/` + +`GET :/s//api/cases/` + +=== Prerequisite + +You must have `read` privileges for the *Cases* feature in the *Management*, +*{observability}*, or *Security* section of the +<>, depending on the +`owner` of the cases you're seeking. + +=== Path parameters + +``:: +(Required, string) An identifier for the case to retrieve. Use +<> to retrieve case IDs. + +``:: +(Optional, string) An identifier for the space. If it is not specified, the +default space is used. + +=== Query parameters + +`includeComments`:: +(Optional, boolean) Determines whether case comments are returned. Defaults to +`true`. deprecated:[8.1.0, "The `includeComments` query parameter is deprecated and will be removed in a future release."] + + +==== Response code + +`200`:: + Indicates a successful call. + +==== Example + +Returns case ID `a18b38a0-71b0-11ea-a0b2-c51ea50a58e2` without comments: + +[source,sh] +-------------------------------------------------- +GET api/cases/a18b38a0-71b0-11ea-a0b2-c51ea50a58e2 +-------------------------------------------------- +// KIBANA + +The API returns a JSON object with the retrieved case. For example: + +[source,json] +-------------------------------------------------- +{ + "id": "a18b38a0-71b0-11ea-a0b2-c51ea50a58e2", + "version": "Wzk4LDFd", + "comments": [], + "totalComment": 0, + "closed_at": null, + "closed_by": null, + "created_at": "2020-03-29T11:30:02.658Z", + "created_by": { + "email": "ahunley@imf.usa.gov", + "full_name": "Alan Hunley", + "username": "ahunley" + }, + "external_service": null, + "updated_at": "2020-03-29T12:01:50.244Z", + "updated_by": { + "full_name": "Classified", + "email": "classified@hms.oo.gov.uk", + "username": "M" + }, + "description": "James Bond clicked on a highly suspicious email banner advertising cheap holidays for underpaid civil servants. Operation bubblegum is active. Repeat - operation bubblegum is now active!", + "title": "This case will self-destruct in 5 seconds", + "status": "open", + "connector": { + "id": "131d4448-abe0-4789-939d-8ef60680b498", + "name": "My connector", + "type": ".jira", + "fields": { + "issueType": "10006", + "priority": "High", + } + }, + "settings": { + "syncAlerts": true + }, + "owner": "securitySolution", + "tags": [ + "phishing", + "social engineering", + "bubblegum" + ] +} +-------------------------------------------------- \ No newline at end of file From 5fda6d56988a549297211ecaf70b6e9885ec67c9 Mon Sep 17 00:00:00 2001 From: Esteban Beltran Date: Tue, 29 Mar 2022 17:31:28 +0200 Subject: [PATCH 31/37] [Cases] Show red color when overing the delete button in the cases table (#128718) --- x-pack/plugins/cases/public/components/all_cases/actions.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/cases/public/components/all_cases/actions.tsx b/x-pack/plugins/cases/public/components/all_cases/actions.tsx index d9e0e0ef025c8..f978cd1b3f205 100644 --- a/x-pack/plugins/cases/public/components/all_cases/actions.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/actions.tsx @@ -20,6 +20,7 @@ export const getActions = ({ { description: i18n.DELETE_CASE(), icon: 'trash', + color: 'danger', name: i18n.DELETE_CASE(), onClick: deleteCaseOnClick, type: 'icon', From 3bd3eb14c79c85991bba040f421810a90be39dbd Mon Sep 17 00:00:00 2001 From: Rickyanto Ang Date: Tue, 29 Mar 2022 08:42:03 -0700 Subject: [PATCH 32/37] UI Polish for Session viewer frame + buttons (#128188) * UI Polish for Session viewer frame + buttons * UI Update for Session Viewer + Toolbar * added Left Borders on Details panel + Fix Details panel button behaviour * Details Panel UI Polish + PR Comments * Find results interaction UI Polish, Def/Min/Max width for details panel, Timestamp bug fix * more conflict fixes * removed unused variable * fix for failed checks on buildkite #1 * Addressing PR comments * PR Comments + Search bar UI bug fix * pr comments Co-authored-by: Karl Godard --- .../detail_panel_accordion/styles.ts | 3 + .../detail_panel_description_list/styles.ts | 4 +- .../detail_panel_list_item/styles.ts | 9 +- .../public/components/process_tree/styles.ts | 2 + .../components/process_tree_node/index.tsx | 16 +- .../components/process_tree_node/styles.ts | 33 ++- .../process_tree_node/use_button_styles.ts | 20 +- .../public/components/session_view/index.tsx | 245 +++++++++--------- .../public/components/session_view/styles.ts | 19 +- .../session_view_detail_panel/index.tsx | 7 +- .../session_view_detail_panel/styles.ts | 25 ++ .../session_view_search_bar/index.tsx | 17 +- .../session_view_search_bar/styles.ts | 16 +- 13 files changed, 255 insertions(+), 161 deletions(-) create mode 100644 x-pack/plugins/session_view/public/components/session_view_detail_panel/styles.ts diff --git a/x-pack/plugins/session_view/public/components/detail_panel_accordion/styles.ts b/x-pack/plugins/session_view/public/components/detail_panel_accordion/styles.ts index c44e069c05c00..96eddb2b2bf98 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_accordion/styles.ts +++ b/x-pack/plugins/session_view/public/components/detail_panel_accordion/styles.ts @@ -22,6 +22,9 @@ export const useStyles = () => { '&:last-child': { borderBottom: euiTheme.border.thin, }, + dl: { + paddingTop: '0px', + }, }; const accordionButton: CSSObject = { diff --git a/x-pack/plugins/session_view/public/components/detail_panel_description_list/styles.ts b/x-pack/plugins/session_view/public/components/detail_panel_description_list/styles.ts index d815cb2a48283..d1f3198a10c85 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_description_list/styles.ts +++ b/x-pack/plugins/session_view/public/components/detail_panel_description_list/styles.ts @@ -14,19 +14,21 @@ export const useStyles = () => { const cached = useMemo(() => { const descriptionList: CSSObject = { - padding: euiTheme.size.s, + padding: `${euiTheme.size.base} ${euiTheme.size.s} `, }; const tabListTitle = { width: '40%', display: 'flex', alignItems: 'center', + marginTop: '0px', }; const tabListDescription = { width: '60%', display: 'flex', alignItems: 'center', + marginTop: '0px', }; return { diff --git a/x-pack/plugins/session_view/public/components/detail_panel_list_item/styles.ts b/x-pack/plugins/session_view/public/components/detail_panel_list_item/styles.ts index c370bd8adb6e2..22f5e6782288f 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_list_item/styles.ts +++ b/x-pack/plugins/session_view/public/components/detail_panel_list_item/styles.ts @@ -20,11 +20,13 @@ export const useStyles = ({ display }: StylesDeps) => { const item: CSSObject = { display, alignItems: 'center', - padding: euiTheme.size.s, + padding: `0px ${euiTheme.size.s} `, width: '100%', - fontSize: 'inherit', fontWeight: 'inherit', - minHeight: '36px', + height: euiTheme.size.xl, + lineHeight: euiTheme.size.l, + letterSpacing: '0px', + textAlign: 'left', }; const copiableItem: CSSObject = { @@ -34,6 +36,7 @@ export const useStyles = ({ display }: StylesDeps) => { '&:hover': { background: transparentize(euiTheme.colors.primary, 0.1), }, + height: '100%', }; return { diff --git a/x-pack/plugins/session_view/public/components/process_tree/styles.ts b/x-pack/plugins/session_view/public/components/process_tree/styles.ts index 207cc55e49582..ed868b7203ccd 100644 --- a/x-pack/plugins/session_view/public/components/process_tree/styles.ts +++ b/x-pack/plugins/session_view/public/components/process_tree/styles.ts @@ -22,6 +22,8 @@ export const useStyles = () => { overflow: 'auto', height: '100%', backgroundColor: colors.lightestShade, + paddingTop: size.base, + paddingLeft: size.s, }; const selectionArea: CSSObject = { diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx b/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx index d2e5b1b899553..9d5a3b1c953cf 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx +++ b/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx @@ -86,7 +86,8 @@ export function ProcessTreeNode({ ), [hasAlerts, alerts, jumpToAlertID] ); - const styles = useStyles({ depth, hasAlerts, hasInvestigatedAlert }); + const isSelected = selectedProcessId === process.id; + const styles = useStyles({ depth, hasAlerts, hasInvestigatedAlert, isSelected }); const buttonStyles = useButtonStyles({}); const nodeRef = useVisible({ @@ -249,15 +250,12 @@ export function ProcessTreeNode({ [exit_code: {exitCode}] )} - {timeStampOn && ( - - {timeStampsNormal} - - )} + {timeStampOn && ( + + {timeStampsNormal} + + )} )} diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/styles.ts b/x-pack/plugins/session_view/public/components/process_tree_node/styles.ts index 55afe5c28071a..c3122294e44fd 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_node/styles.ts +++ b/x-pack/plugins/session_view/public/components/process_tree_node/styles.ts @@ -13,9 +13,10 @@ interface StylesDeps { depth: number; hasAlerts: boolean; hasInvestigatedAlert: boolean; + isSelected: boolean; } -export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert }: StylesDeps) => { +export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert, isSelected }: StylesDeps) => { const { euiTheme } = useEuiTheme(); const cached = useMemo(() => { @@ -25,14 +26,11 @@ export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert }: StylesDeps const darkText: CSSObject = { color: colors.text, + fontFamily: font.familyCode, + paddingLeft: size.xxs, + paddingRight: size.xs, }; - const searchHighlight = ` - background-color: ${colors.highlight}; - color: ${colors.fullShade}; - border-radius: ${border.radius.medium}; - `; - const children: CSSObject = { position: 'relative', color: colors.ghost, @@ -48,6 +46,7 @@ export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert }: StylesDeps let bgColor = 'none'; const hoverColor = transparentize(colors.primary, 0.04); let borderColor = 'transparent'; + let searchResColor = transparentize(colors.warning, 0.32); if (hasAlerts) { borderColor = colors.danger; @@ -57,10 +56,14 @@ export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert }: StylesDeps bgColor = transparentize(colors.danger, 0.04); } - return { bgColor, borderColor, hoverColor }; + if (isSelected) { + searchResColor = colors.warning; + } + + return { bgColor, borderColor, hoverColor, searchResColor }; }; - const { bgColor, borderColor, hoverColor } = getHighlightColors(); + const { bgColor, borderColor, hoverColor, searchResColor } = getHighlightColors(); const processNode: CSSObject = { display: 'block', @@ -84,6 +87,12 @@ export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert }: StylesDeps }, }; + const searchHighlight = ` + color: ${colors.fullShade}; + border-radius: '0px'; + background-color: ${searchResColor}; + `; + const wrapper: CSSObject = { paddingLeft: size.s, position: 'relative', @@ -96,6 +105,10 @@ export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert }: StylesDeps const workingDir: CSSObject = { color: colors.successText, + fontFamily: font.familyCode, + fontWeight: font.weight.medium, + paddingLeft: size.s, + paddingRight: size.xxs, }; const timeStamp: CSSObject = { @@ -124,7 +137,7 @@ export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert }: StylesDeps timeStamp, alertDetails, }; - }, [depth, euiTheme, hasAlerts, hasInvestigatedAlert]); + }, [depth, euiTheme, hasAlerts, hasInvestigatedAlert, isSelected]); return cached; }; diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/use_button_styles.ts b/x-pack/plugins/session_view/public/components/process_tree_node/use_button_styles.ts index 529a0ce5819f9..4c713b42a2d7b 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_node/use_button_styles.ts +++ b/x-pack/plugins/session_view/public/components/process_tree_node/use_button_styles.ts @@ -18,7 +18,7 @@ export const useButtonStyles = ({ isExpanded }: ButtonStylesDeps) => { const { euiTheme } = useEuiTheme(); const cached = useMemo(() => { - const { colors, border, size } = euiTheme; + const { colors, border, size, font } = euiTheme; const button: CSSObject = { background: transparentize(theme.euiColorVis6, 0.04), @@ -26,14 +26,21 @@ export const useButtonStyles = ({ isExpanded }: ButtonStylesDeps) => { lineHeight: '18px', height: '20px', fontSize: size.m, - borderRadius: border.radius.medium, + fontFamily: font.family, + fontWeight: font.weight.medium, + borderRadius: border.radius.small, color: shade(theme.euiColorVis6, 0.25), - marginLeft: size.s, + marginLeft: size.xs, + marginRight: size.xs, minWidth: 0, + padding: `${size.s} ${size.xxs}`, + span: { + padding: `0px ${size.xxs} !important`, + }, }; const buttonArrow: CSSObject = { - marginLeft: size.s, + marginLeft: size.xs, }; const alertButton: CSSObject = { @@ -72,6 +79,10 @@ export const useButtonStyles = ({ isExpanded }: ButtonStylesDeps) => { textTransform: 'capitalize', }; + const buttonSize: CSSObject = { + padding: `0px ${euiTheme.size.xs}`, + }; + const expandedIcon = isExpanded ? 'arrowUp' : 'arrowDown'; return { @@ -81,6 +92,7 @@ export const useButtonStyles = ({ isExpanded }: ButtonStylesDeps) => { alertsCountNumber, userChangedButton, userChangedButtonUsername, + buttonSize, expandedIcon, }; }, [euiTheme, isExpanded]); diff --git a/x-pack/plugins/session_view/public/components/session_view/index.tsx b/x-pack/plugins/session_view/public/components/session_view/index.tsx index 1ec9441a2b1d1..2e1a598faedaa 100644 --- a/x-pack/plugins/session_view/public/components/session_view/index.tsx +++ b/x-pack/plugins/session_view/public/components/session_view/index.tsx @@ -12,6 +12,7 @@ import { EuiFlexItem, EuiResizableContainer, EuiPanel, + EuiHorizontalRule, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { SectionLoading } from '../../shared_imports'; @@ -132,133 +133,137 @@ export const SessionView = ({ return ( <> - - - - - - - - - - - - + + + - - - - - - - {(EuiResizablePanel, EuiResizableButton) => ( - <> - - {renderIsLoading && ( - - - - )} +
- {hasError && ( - - - - } - body={ -

- -

- } + + + + + + + - )} + + +
+ + + + {(EuiResizablePanel, EuiResizableButton) => ( + <> + + {renderIsLoading && ( + + + + )} - {hasData && ( -
- + + + } + body={ +

+ +

+ } /> -
- )} -
+ )} - {renderDetails ? ( - <> - - - - - - ) : ( - <> - {/* Returning an empty element here (instead of false) to avoid a bug in EuiResizableContainer */} - - )} - - )} -
+ {hasData && ( +
+ +
+ )} + + + {renderDetails ? ( + <> + + + + + + ) : ( + <> + {/* Returning an empty element here (instead of false) to avoid a bug in EuiResizableContainer */} + + )} + + )} + + ); }; diff --git a/x-pack/plugins/session_view/public/components/session_view/styles.ts b/x-pack/plugins/session_view/public/components/session_view/styles.ts index edfe2356d5aa2..3ca0594f57574 100644 --- a/x-pack/plugins/session_view/public/components/session_view/styles.ts +++ b/x-pack/plugins/session_view/public/components/session_view/styles.ts @@ -8,6 +8,7 @@ import { useMemo } from 'react'; import { useEuiTheme } from '@elastic/eui'; import { CSSObject } from '@emotion/react'; +import { euiLightVars as theme } from '@kbn/ui-theme'; interface StylesDeps { height: number | undefined; @@ -17,9 +18,7 @@ export const useStyles = ({ height = 500 }: StylesDeps) => { const { euiTheme } = useEuiTheme(); const cached = useMemo(() => { - const { border, colors } = euiTheme; - - const thinBorder = `${border.width.thin} solid ${colors.lightShade}!important`; + const { border } = euiTheme; const processTree: CSSObject = { height: `${height}px`, @@ -28,8 +27,7 @@ export const useStyles = ({ height = 500 }: StylesDeps) => { const detailPanel: CSSObject = { height: `${height}px`, - borderLeft: thinBorder, - borderRight: thinBorder, + borderRightWidth: '0px', }; const resizeHandle: CSSObject = { @@ -45,12 +43,23 @@ export const useStyles = ({ height = 500 }: StylesDeps) => { margin: `${euiTheme.size.m} ${euiTheme.size.xs} !important`, }; + const sessionViewerComponent: CSSObject = { + border: border.thin, + borderRadius: border.radius.medium, + }; + + const toolBar: CSSObject = { + backgroundColor: `${theme.euiFormBackgroundDisabledColor} !important`, + }; + return { processTree, detailPanel, resizeHandle, searchBar, buttonsEyeDetail, + sessionViewerComponent, + toolBar, }; }, [height, euiTheme]); diff --git a/x-pack/plugins/session_view/public/components/session_view_detail_panel/index.tsx b/x-pack/plugins/session_view/public/components/session_view_detail_panel/index.tsx index 51eb65a38f835..e24409a98f8fd 100644 --- a/x-pack/plugins/session_view/public/components/session_view_detail_panel/index.tsx +++ b/x-pack/plugins/session_view/public/components/session_view_detail_panel/index.tsx @@ -12,6 +12,7 @@ import { Process, ProcessEvent } from '../../../common/types/process_tree'; import { getDetailPanelProcess, getSelectedTabContent } from './helpers'; import { DetailPanelProcessTab } from '../detail_panel_process_tab'; import { DetailPanelHostTab } from '../detail_panel_host_tab'; +import { useStyles } from './styles'; import { DetailPanelAlertTab } from '../detail_panel_alert_tab'; import { ALERT_COUNT_THRESHOLD } from '../../../common/constants'; @@ -101,8 +102,10 @@ export const SessionViewDetailPanel = ({ [tabs, selectedTabId] ); + const styles = useStyles(); + return ( - <> +
{tabs.map((tab, index) => ( {tabContent} - +
); }; diff --git a/x-pack/plugins/session_view/public/components/session_view_detail_panel/styles.ts b/x-pack/plugins/session_view/public/components/session_view_detail_panel/styles.ts new file mode 100644 index 0000000000000..fbb196da3fa80 --- /dev/null +++ b/x-pack/plugins/session_view/public/components/session_view_detail_panel/styles.ts @@ -0,0 +1,25 @@ +/* + * 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 { useMemo } from 'react'; +import { useEuiTheme } from '@elastic/eui'; +import { CSSObject } from '@emotion/react'; + +export const useStyles = () => { + const { euiTheme } = useEuiTheme(); + const cached = useMemo(() => { + const detailsPanelLeftBorder: CSSObject = { + borderLeft: euiTheme.border.thin, + }; + + return { + detailsPanelLeftBorder, + }; + }, [euiTheme]); + + return cached; +}; diff --git a/x-pack/plugins/session_view/public/components/session_view_search_bar/index.tsx b/x-pack/plugins/session_view/public/components/session_view_search_bar/index.tsx index f4e4dac7a94c7..05154fca40769 100644 --- a/x-pack/plugins/session_view/public/components/session_view_search_bar/index.tsx +++ b/x-pack/plugins/session_view/public/components/session_view_search_bar/index.tsx @@ -7,6 +7,7 @@ import React, { useState, useEffect } from 'react'; import { EuiSearchBar, EuiPagination } from '@elastic/eui'; import { EuiSearchBarOnChangeArgs } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { Process } from '../../../common/types/process_tree'; import { useStyles } from './styles'; @@ -17,6 +18,12 @@ interface SessionViewSearchBarDeps { onProcessSelected(process: Process): void; } +const translatePlaceholder = { + placeholder: i18n.translate('xpack.sessionView.searchBar.searchBarKeyPlaceholder', { + defaultMessage: 'Find...', + }), +}; + /** * The main wrapper component for the session view. */ @@ -26,7 +33,9 @@ export const SessionViewSearchBar = ({ onProcessSelected, searchResults, }: SessionViewSearchBarDeps) => { - const styles = useStyles(); + const showPagination = !!searchResults?.length; + + const styles = useStyles({ hasSearchResults: showPagination }); const [selectedResult, setSelectedResult] = useState(0); @@ -50,11 +59,9 @@ export const SessionViewSearchBar = ({ } }, [searchResults, onProcessSelected, selectedResult]); - const showPagination = !!searchResults?.length; - return ( -
- +
+ {showPagination && ( { +interface StylesDeps { + hasSearchResults: boolean; +} + +export const useStyles = ({ hasSearchResults }: StylesDeps) => { const { euiTheme } = useEuiTheme(); const cached = useMemo(() => { @@ -19,10 +23,18 @@ export const useStyles = () => { right: euiTheme.size.xxl, }; + const searchBarWithResult: CSSObject = { + position: 'relative', + 'input.euiFieldSearch.euiFieldSearch-isClearable': { + paddingRight: hasSearchResults ? '200px' : euiTheme.size.xxl, + }, + }; + return { pagination, + searchBarWithResult, }; - }, [euiTheme]); + }, [euiTheme, hasSearchResults]); return cached; }; From 9f10418f34a1984665bcdc54098204544c1a51c2 Mon Sep 17 00:00:00 2001 From: Sander Philipse <94373878+sphilipse@users.noreply.github.com> Date: Tue, 29 Mar 2022 17:43:01 +0200 Subject: [PATCH 33/37] Sort content sources and connectors by display name (#128745) * Sort content sources and connectors by display name --- .../workplace_search/utils/index.ts | 1 + .../utils/sort_by_name.test.ts | 42 +++++++++++++++++++ .../workplace_search/utils/sort_by_name.ts | 14 +++++++ .../views/content_sources/sources_logic.ts | 28 +++++++------ .../views/settings/settings_logic.ts | 3 +- 5 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/sort_by_name.test.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/sort_by_name.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/index.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/index.ts index 86d3e4f844bbd..c66a6d1ca0fc0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/index.ts @@ -13,3 +13,4 @@ export { readUploadedFileAsText } from './read_uploaded_file_as_text'; export { handlePrivateKeyUpload } from './handle_private_key_upload'; export { hasMultipleConnectorOptions } from './has_multiple_connector_options'; export { isNotNullish } from './is_not_nullish'; +export { sortByName } from './sort_by_name'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/sort_by_name.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/sort_by_name.test.ts new file mode 100644 index 0000000000000..185eeedda0512 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/sort_by_name.test.ts @@ -0,0 +1,42 @@ +/* + * 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 { sortByName } from './sort_by_name'; + +describe('sortByName', () => { + it('should sort by name', () => { + const unsorted = [ + { + name: 'aba', + }, + { + name: 'aaa', + }, + { + name: 'constant', + }, + { + name: 'beta', + }, + ]; + const sorted = [ + { + name: 'aaa', + }, + { + name: 'aba', + }, + { + name: 'beta', + }, + { + name: 'constant', + }, + ]; + expect(sortByName(unsorted)).toEqual(sorted); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/sort_by_name.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/sort_by_name.ts new file mode 100644 index 0000000000000..cb2f85b245166 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/sort_by_name.ts @@ -0,0 +1,14 @@ +/* + * 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. + */ + +interface WithName { + name: string; +} + +export function sortByName(nameItems: T[]): T[] { + return [...nameItems].sort((a, b) => a.name.localeCompare(b.name)); +} diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.ts index 1651411fb9c5d..868831ab7c7fb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.ts @@ -15,6 +15,7 @@ import { flashAPIErrors, flashSuccessToast } from '../../../shared/flash_message import { HttpLogic } from '../../../shared/http'; import { AppLogic } from '../../app_logic'; import { Connector, ContentSourceDetails, ContentSourceStatus, SourceDataItem } from '../../types'; +import { sortByName } from '../../utils'; import { staticSourceData } from './source_data'; @@ -50,7 +51,7 @@ export interface IPermissionsModalProps { additionalConfiguration: boolean; } -type CombinedDataItem = SourceDataItem & ContentSourceDetails; +type CombinedDataItem = SourceDataItem & { connected: boolean }; export interface ISourcesValues { contentSources: ContentSourceDetails[]; @@ -144,11 +145,13 @@ export const SourcesLogic = kea>( selectors: ({ selectors }) => ({ availableSources: [ () => [selectors.sourceData], - (sourceData: SourceDataItem[]) => sourceData.filter(({ configured }) => !configured), + (sourceData: SourceDataItem[]) => + sortByName(sourceData.filter(({ configured }) => !configured)), ], configuredSources: [ () => [selectors.sourceData], - (sourceData: SourceDataItem[]) => sourceData.filter(({ configured }) => configured), + (sourceData: SourceDataItem[]) => + sortByName(sourceData.filter(({ configured }) => configured)), ], externalConfigured: [ () => [selectors.configuredSources], @@ -307,18 +310,17 @@ export const mergeServerAndStaticData = ( serverData: Connector[], staticData: SourceDataItem[], contentSources: ContentSourceDetails[] -) => { - const combined = [] as CombinedDataItem[]; - staticData.forEach((staticItem) => { - const type = staticItem.serviceType; - const serverItem = serverData.find(({ serviceType }) => serviceType === type); - const connectedSource = contentSources.find(({ serviceType }) => serviceType === type); - combined.push({ +): CombinedDataItem[] => { + const unsortedData = staticData.map((staticItem) => { + const serverItem = serverData.find(({ serviceType }) => serviceType === staticItem.serviceType); + const connectedSource = contentSources.find( + ({ serviceType }) => serviceType === staticItem.serviceType + ); + return { ...staticItem, ...serverItem, connected: !!connectedSource, - } as CombinedDataItem); + }; }); - - return combined; + return sortByName(unsortedData); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/settings_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/settings_logic.ts index 64dfa3f8e13bb..8c6dee4cf7f9c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/settings_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/settings_logic.ts @@ -20,6 +20,7 @@ import { AppLogic } from '../../app_logic'; import { ORG_UPDATED_MESSAGE, OAUTH_APP_UPDATED_MESSAGE } from '../../constants'; import { ORG_SETTINGS_CONNECTORS_PATH } from '../../routes'; import { Connector } from '../../types'; +import { sortByName } from '../../utils'; interface IOauthApplication { name: string; @@ -118,7 +119,7 @@ export const SettingsLogic = kea> connectors: [ [], { - onInitializeConnectors: (_, connectors) => connectors, + onInitializeConnectors: (_, connectors) => sortByName(connectors), }, ], orgNameInputValue: [ From ac4e96c606106f9abc7341b280134f01ab4f1d76 Mon Sep 17 00:00:00 2001 From: Marshall Main <55718608+marshallmain@users.noreply.github.com> Date: Tue, 29 Mar 2022 08:59:53 -0700 Subject: [PATCH 34/37] [Security Solution][Alerts] Remove dead legacy signals code (#128328) * Remove dead legacy signals code * Remove unused import * Remove extra param --- ...eate_persistence_rule_type_wrapper.mock.ts | 22 + .../create_security_rule_type_wrapper.ts | 32 +- .../build_alert_group_from_sequence.test.ts | 344 +++++++++++++- .../utils/build_alert_group_from_sequence.ts | 52 ++- .../signals/build_bulk_body.ts | 251 ---------- .../signals/build_event_type_signal.test.ts | 101 ---- .../signals/build_event_type_signal.ts | 30 -- .../signals/build_rule.test.ts | 189 -------- .../detection_engine/signals/build_rule.ts | 91 ---- .../signals/build_signal.test.ts | 376 --------------- .../detection_engine/signals/build_signal.ts | 130 ------ .../signals/bulk_create_factory.ts | 107 ----- .../signals/bulk_create_ml_signals.ts | 2 +- .../signals/executors/query.ts | 1 - .../detection_engine/signals/get_filter.ts | 4 +- .../signals/search_after_bulk_create.test.ts | 441 ++++-------------- .../signals/search_after_bulk_create.ts | 3 +- .../threat_mapping/create_event_signal.ts | 1 - .../threat_mapping/create_threat_signal.ts | 1 - .../bulk_create_threshold_signals.test.ts | 20 +- .../bulk_create_threshold_signals.ts | 31 +- .../lib/detection_engine/signals/types.ts | 13 +- .../signals/wrap_hits_factory.ts | 37 -- .../signals/wrap_sequences_factory.ts | 39 -- 24 files changed, 539 insertions(+), 1779 deletions(-) create mode 100644 x-pack/plugins/rule_registry/server/utils/create_persistence_rule_type_wrapper.mock.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_event_type_signal.test.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_event_type_signal.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.test.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_signal.test.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_signal.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_factory.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/wrap_hits_factory.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/wrap_sequences_factory.ts diff --git a/x-pack/plugins/rule_registry/server/utils/create_persistence_rule_type_wrapper.mock.ts b/x-pack/plugins/rule_registry/server/utils/create_persistence_rule_type_wrapper.mock.ts new file mode 100644 index 0000000000000..86ef14e491f4e --- /dev/null +++ b/x-pack/plugins/rule_registry/server/utils/create_persistence_rule_type_wrapper.mock.ts @@ -0,0 +1,22 @@ +/* + * 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 { alertsMock } from '../../../alerting/server/mocks'; +import { PersistenceServices } from './persistence_types'; + +export const createPersistenceServicesMock = (): jest.Mocked => { + return { + alertWithPersistence: jest.fn(), + }; +}; + +export const createPersistenceExecutorOptionsMock = () => { + return { + ...alertsMock.createAlertServices(), + ...createPersistenceServicesMock(), + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts index 38300dff14558..f25bb16e90004 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts @@ -8,7 +8,6 @@ import { isEmpty } from 'lodash'; import { parseScheduleDates } from '@kbn/securitysolution-io-ts-utils'; -import { ListArray } from '@kbn/securitysolution-io-ts-list-types'; import agent from 'elastic-apm-node'; import { createPersistenceRuleTypeWrapper } from '../../../../../rule_registry/server'; @@ -19,11 +18,7 @@ import { getRuleRangeTuples, hasReadIndexPrivileges, hasTimestampFields, - isEqlParams, - isQueryParams, - isSavedQueryParams, - isThreatParams, - isThresholdParams, + isMachineLearningParams, } from '../signals/utils'; import { DEFAULT_MAX_SIGNALS, DEFAULT_SEARCH_AFTER_PAGE_SIZE } from '../../../../common/constants'; import { CreateSecurityRuleTypeWrapper } from './types'; @@ -133,22 +128,13 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = ...params, name, id: alertId, - } as unknown as NotificationRuleTypeParams; + }; // check if rule has permissions to access given index pattern // move this collection of lines into a function in utils // so that we can use it in create rules route, bulk, etc. try { - // Typescript 4.1.3 can't figure out that `!isMachineLearningParams(params)` also excludes the only rule type - // of rule params that doesn't include `params.index`, but Typescript 4.3.5 does compute the stricter type correctly. - // When we update Typescript to >= 4.3.5, we can replace this logic with `!isMachineLearningParams(params)` again. - if ( - isEqlParams(params) || - isThresholdParams(params) || - isQueryParams(params) || - isSavedQueryParams(params) || - isThreatParams(params) - ) { + if (!isMachineLearningParams(params)) { const index = params.index; const hasTimestampOverride = !!timestampOverride; @@ -170,7 +156,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = { index, fields: hasTimestampOverride - ? ['@timestamp', timestampOverride as string] + ? ['@timestamp', timestampOverride] : ['@timestamp'], include_unmapped: true, }, @@ -178,9 +164,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = ) ); wroteWarningStatus = await hasTimestampFields({ - timestampField: hasTimestampOverride - ? (timestampOverride as string) - : '@timestamp', + timestampField: hasTimestampOverride ? timestampOverride : '@timestamp', timestampFieldCapsResponse: timestampFieldCaps, inputIndices, ruleExecutionLogger, @@ -202,8 +186,8 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = const { tuples, remainingGap } = getRuleRangeTuples({ logger, previousStartedAt, - from: from as string, - to: to as string, + from, + to, interval, maxSignals: maxSignals ?? DEFAULT_MAX_SIGNALS, buildRuleMessage, @@ -236,7 +220,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = const exceptionItems = await getExceptions({ client: exceptionsClient, - lists: (params.exceptionsList as ListArray) ?? [], + lists: params.exceptionsList, }); const bulkCreate = bulkCreateFactory( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.test.ts index 969f7caab6456..b1b68829665fc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.test.ts @@ -10,7 +10,11 @@ import { Logger } from 'kibana/server'; import { ALERT_RULE_CONSUMER } from '@kbn/rule-data-utils'; import { sampleDocNoSortId, sampleRuleGuid } from '../../../signals/__mocks__/es_results'; -import { buildAlertGroupFromSequence } from './build_alert_group_from_sequence'; +import { + buildAlertGroupFromSequence, + objectArrayIntersection, + objectPairIntersection, +} from './build_alert_group_from_sequence'; import { SERVER_APP_ID } from '../../../../../../common/constants'; import { getCompleteRuleMock, getQueryRuleParams } from '../../../schemas/rule_schemas.mock'; import { QueryRuleParams } from '../../../schemas/rule_schemas'; @@ -134,4 +138,342 @@ describe('buildAlert', () => { expect(groupId).toEqual(groupIds[0]); } }); + + describe('recursive intersection between objects', () => { + test('should treat numbers and strings as unequal', () => { + const a = { + field1: 1, + field2: 1, + }; + const b = { + field1: 1, + field2: '1', + }; + const intersection = objectPairIntersection(a, b); + const expected = { + field1: 1, + }; + expect(intersection).toEqual(expected); + }); + + test('should strip unequal numbers and strings', () => { + const a = { + field1: 1, + field2: 1, + field3: 'abcd', + field4: 'abcd', + }; + const b = { + field1: 1, + field2: 100, + field3: 'abcd', + field4: 'wxyz', + }; + const intersection = objectPairIntersection(a, b); + const expected = { + field1: 1, + field3: 'abcd', + }; + expect(intersection).toEqual(expected); + }); + + test('should handle null values', () => { + const a = { + field1: 1, + field2: '1', + field3: null, + }; + const b = { + field1: null, + field2: null, + field3: null, + }; + const intersection = objectPairIntersection(a, b); + const expected = { + field3: null, + }; + expect(intersection).toEqual(expected); + }); + + test('should handle explicit undefined values and return undefined if left with only undefined fields', () => { + const a = { + field1: 1, + field2: '1', + field3: undefined, + }; + const b = { + field1: undefined, + field2: undefined, + field3: undefined, + }; + const intersection = objectPairIntersection(a, b); + const expected = undefined; + expect(intersection).toEqual(expected); + }); + + test('should strip arrays out regardless of whether they are equal', () => { + const a = { + array_field1: [1, 2], + array_field2: [1, 2], + }; + const b = { + array_field1: [1, 2], + array_field2: [3, 4], + }; + const intersection = objectPairIntersection(a, b); + const expected = undefined; + expect(intersection).toEqual(expected); + }); + + test('should strip fields that are not in both objects', () => { + const a = { + field1: 1, + }; + const b = { + field2: 1, + }; + const intersection = objectPairIntersection(a, b); + const expected = undefined; + expect(intersection).toEqual(expected); + }); + + test('should work on objects within objects', () => { + const a = { + container_field: { + field1: 1, + field2: 1, + field3: 10, + field5: 1, + field6: null, + array_field: [1, 2], + nested_container_field: { + field1: 1, + field2: 1, + }, + nested_container_field2: { + field1: undefined, + }, + }, + container_field_without_intersection: { + sub_field1: 1, + }, + }; + const b = { + container_field: { + field1: 1, + field2: 2, + field4: 10, + field5: '1', + field6: null, + array_field: [1, 2], + nested_container_field: { + field1: 1, + field2: 2, + }, + nested_container_field2: { + field1: undefined, + }, + }, + container_field_without_intersection: { + sub_field2: 1, + }, + }; + const intersection = objectPairIntersection(a, b); + const expected = { + container_field: { + field1: 1, + field6: null, + nested_container_field: { + field1: 1, + }, + }, + }; + expect(intersection).toEqual(expected); + }); + + test('should work on objects with a variety of fields', () => { + const a = { + field1: 1, + field2: 1, + field3: 10, + field5: 1, + field6: null, + array_field: [1, 2], + container_field: { + sub_field1: 1, + sub_field2: 1, + sub_field3: 10, + }, + container_field_without_intersection: { + sub_field1: 1, + }, + }; + const b = { + field1: 1, + field2: 2, + field4: 10, + field5: '1', + field6: null, + array_field: [1, 2], + container_field: { + sub_field1: 1, + sub_field2: 2, + sub_field4: 10, + }, + container_field_without_intersection: { + sub_field2: 1, + }, + }; + const intersection = objectPairIntersection(a, b); + const expected = { + field1: 1, + field6: null, + container_field: { + sub_field1: 1, + }, + }; + expect(intersection).toEqual(expected); + }); + }); + + describe('objectArrayIntersection', () => { + test('should return undefined if the array is empty', () => { + const intersection = objectArrayIntersection([]); + const expected = undefined; + expect(intersection).toEqual(expected); + }); + test('should return the initial object if there is only 1', () => { + const a = { + field1: 1, + field2: 1, + field3: 10, + field5: 1, + field6: null, + array_field: [1, 2], + container_field: { + sub_field1: 1, + sub_field2: 1, + sub_field3: 10, + }, + container_field_without_intersection: { + sub_field1: 1, + }, + }; + const intersection = objectArrayIntersection([a]); + const expected = { + field1: 1, + field2: 1, + field3: 10, + field5: 1, + field6: null, + array_field: [1, 2], + container_field: { + sub_field1: 1, + sub_field2: 1, + sub_field3: 10, + }, + container_field_without_intersection: { + sub_field1: 1, + }, + }; + expect(intersection).toEqual(expected); + }); + test('should work with exactly 2 objects', () => { + const a = { + field1: 1, + field2: 1, + field3: 10, + field5: 1, + field6: null, + array_field: [1, 2], + container_field: { + sub_field1: 1, + sub_field2: 1, + sub_field3: 10, + }, + container_field_without_intersection: { + sub_field1: 1, + }, + }; + const b = { + field1: 1, + field2: 2, + field4: 10, + field5: '1', + field6: null, + array_field: [1, 2], + container_field: { + sub_field1: 1, + sub_field2: 2, + sub_field4: 10, + }, + container_field_without_intersection: { + sub_field2: 1, + }, + }; + const intersection = objectArrayIntersection([a, b]); + const expected = { + field1: 1, + field6: null, + container_field: { + sub_field1: 1, + }, + }; + expect(intersection).toEqual(expected); + }); + + test('should work with 3 or more objects', () => { + const a = { + field1: 1, + field2: 1, + field3: 10, + field5: 1, + field6: null, + array_field: [1, 2], + container_field: { + sub_field1: 1, + sub_field2: 1, + sub_field3: 10, + }, + container_field_without_intersection: { + sub_field1: 1, + }, + }; + const b = { + field1: 1, + field2: 2, + field4: 10, + field5: '1', + field6: null, + array_field: [1, 2], + container_field: { + sub_field1: 1, + sub_field2: 2, + sub_field4: 10, + }, + container_field_without_intersection: { + sub_field2: 1, + }, + }; + const c = { + field1: 1, + field2: 2, + field4: 10, + field5: '1', + array_field: [1, 2], + container_field: { + sub_field2: 2, + sub_field4: 10, + }, + container_field_without_intersection: { + sub_field2: 1, + }, + }; + const intersection = objectArrayIntersection([a, b, c]); + const expected = { + field1: 1, + }; + expect(intersection).toEqual(expected); + }); + }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.ts index 180494f9209dd..26e0289732bfb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.ts @@ -16,7 +16,6 @@ import { buildAlert, buildAncestors, generateAlertId } from './build_alert'; import { buildBulkBody } from './build_bulk_body'; import { EqlSequence } from '../../../../../../common/detection_engine/types'; import { generateBuildingBlockIds } from './generate_building_block_ids'; -import { objectArrayIntersection } from '../../../signals/build_bulk_body'; import { BuildReasonMessage } from '../../../signals/reason_formatters'; import { CompleteRule, RuleParams } from '../../../schemas/rule_schemas'; import { @@ -118,3 +117,54 @@ export const buildAlertRoot = ( [ALERT_GROUP_ID]: generateAlertId(doc), }; }; + +export const objectArrayIntersection = (objects: object[]) => { + if (objects.length === 0) { + return undefined; + } else if (objects.length === 1) { + return objects[0]; + } else { + return objects + .slice(1) + .reduce( + (acc: object | undefined, obj): object | undefined => objectPairIntersection(acc, obj), + objects[0] + ); + } +}; + +export const objectPairIntersection = (a: object | undefined, b: object | undefined) => { + if (a === undefined || b === undefined) { + return undefined; + } + const intersection: Record = {}; + Object.entries(a).forEach(([key, aVal]) => { + if (key in b) { + const bVal = (b as Record)[key]; + if ( + typeof aVal === 'object' && + !(aVal instanceof Array) && + aVal !== null && + typeof bVal === 'object' && + !(bVal instanceof Array) && + bVal !== null + ) { + intersection[key] = objectPairIntersection(aVal, bVal); + } else if (aVal === bVal) { + intersection[key] = aVal; + } + } + }); + // Count up the number of entries that are NOT undefined in the intersection + // If there are no keys OR all entries are undefined, return undefined + if ( + Object.values(intersection).reduce( + (acc: number, value) => (value !== undefined ? acc + 1 : acc), + 0 + ) === 0 + ) { + return undefined; + } else { + return intersection; + } +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts deleted file mode 100644 index 21bfced47df42..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts +++ /dev/null @@ -1,251 +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 { TIMESTAMP } from '@kbn/rule-data-utils'; -import { getMergeStrategy } from './source_fields_merging/strategies'; -import { - SignalSourceHit, - SignalHit, - Signal, - BaseSignalHit, - SignalSource, - WrappedSignalHit, -} from './types'; -import { buildRuleWithoutOverrides, buildRuleWithOverrides } from './build_rule'; -import { additionalSignalFields, buildSignal } from './build_signal'; -import { buildEventTypeSignal } from './build_event_type_signal'; -import { EqlSequence } from '../../../../common/detection_engine/types'; -import { generateSignalId, wrapBuildingBlocks, wrapSignal } from './utils'; -import type { ConfigType } from '../../../config'; -import { BuildReasonMessage } from './reason_formatters'; -import { CompleteRule, RuleParams } from '../schemas/rule_schemas'; - -/** - * Formats the search_after result for insertion into the signals index. We first create a - * "best effort" merged "fields" with the "_source" object, then build the signal object, - * then the event object, and finally we strip away any additional temporary data that was added - * such as the "threshold_result". - * @param completeRule The rule object to build overrides - * @param doc The SignalSourceHit with "_source", "fields", and additional data such as "threshold_result" - * @returns The body that can be added to a bulk call for inserting the signal. - */ -export const buildBulkBody = ( - completeRule: CompleteRule, - doc: SignalSourceHit, - mergeStrategy: ConfigType['alertMergeStrategy'], - ignoreFields: ConfigType['alertIgnoreFields'], - buildReasonMessage: BuildReasonMessage -): SignalHit => { - const mergedDoc = getMergeStrategy(mergeStrategy)({ doc, ignoreFields }); - const rule = buildRuleWithOverrides(completeRule, mergedDoc._source ?? {}); - const timestamp = new Date().toISOString(); - const reason = buildReasonMessage({ - name: completeRule.ruleConfig.name, - severity: completeRule.ruleParams.severity, - mergedDoc, - }); - const signal: Signal = { - ...buildSignal([mergedDoc], rule, reason), - ...additionalSignalFields(mergedDoc), - }; - const event = buildEventTypeSignal(mergedDoc); - // Filter out any kibana.* fields from the generated signal - kibana.* fields are aliases - // in siem-signals so we can't write to them, but for signals-on-signals they'll be returned - // in the fields API response and merged into the mergedDoc source - const { - threshold_result: thresholdResult, - kibana, - ...filteredSource - } = mergedDoc._source || { - threshold_result: null, - }; - const signalHit: SignalHit = { - ...filteredSource, - [TIMESTAMP]: timestamp, - event, - signal, - }; - return signalHit; -}; - -/** - * Takes N raw documents from ES that form a sequence and builds them into N+1 signals ready to be indexed - - * one signal for each event in the sequence, and a "shell" signal that ties them all together. All N+1 signals - * share the same signal.group.id to make it easy to query them. - * @param sequence The raw ES documents that make up the sequence - * @param completeRule rule object representing the rule that found the sequence - * @param outputIndex Index to write the resulting signals to - */ -export const buildSignalGroupFromSequence = ( - sequence: EqlSequence, - completeRule: CompleteRule, - outputIndex: string, - mergeStrategy: ConfigType['alertMergeStrategy'], - ignoreFields: ConfigType['alertIgnoreFields'], - buildReasonMessage: BuildReasonMessage -): WrappedSignalHit[] => { - const wrappedBuildingBlocks = wrapBuildingBlocks( - sequence.events.map((event) => { - const signal = buildSignalFromEvent( - event, - completeRule, - false, - mergeStrategy, - ignoreFields, - buildReasonMessage - ); - signal.signal.rule.building_block_type = 'default'; - return signal; - }), - outputIndex - ); - - if ( - wrappedBuildingBlocks.some((block) => - block._source.signal?.ancestors.some((ancestor) => ancestor.rule === completeRule.alertId) - ) - ) { - return []; - } - - // Now that we have an array of building blocks for the events in the sequence, - // we can build the signal that links the building blocks together - // and also insert the group id (which is also the "shell" signal _id) in each building block - const sequenceSignal = wrapSignal( - buildSignalFromSequence(wrappedBuildingBlocks, completeRule, buildReasonMessage), - outputIndex - ); - wrappedBuildingBlocks.forEach((block, idx) => { - // TODO: fix type of blocks so we don't have to check existence of _source.signal - if (block._source.signal) { - block._source.signal.group = { - id: sequenceSignal._id, - index: idx, - }; - } - }); - return [...wrappedBuildingBlocks, sequenceSignal]; -}; - -export const buildSignalFromSequence = ( - events: WrappedSignalHit[], - completeRule: CompleteRule, - buildReasonMessage: BuildReasonMessage -): SignalHit => { - const rule = buildRuleWithoutOverrides(completeRule); - const timestamp = new Date().toISOString(); - const mergedEvents = objectArrayIntersection(events.map((event) => event._source)); - const reason = buildReasonMessage({ - name: completeRule.ruleConfig.name, - severity: completeRule.ruleParams.severity, - mergedDoc: mergedEvents as SignalSourceHit, - }); - const signal: Signal = buildSignal(events, rule, reason); - return { - ...mergedEvents, - [TIMESTAMP]: timestamp, - event: { - kind: 'signal', - }, - signal: { - ...signal, - group: { - // This is the same function that is used later to generate the _id for the sequence signal document, - // so _id should equal signal.group.id for the "shell" document - id: generateSignalId(signal), - }, - }, - }; -}; - -export const buildSignalFromEvent = ( - event: BaseSignalHit, - completeRule: CompleteRule, - applyOverrides: boolean, - mergeStrategy: ConfigType['alertMergeStrategy'], - ignoreFields: ConfigType['alertIgnoreFields'], - buildReasonMessage: BuildReasonMessage -): SignalHit => { - const mergedEvent = getMergeStrategy(mergeStrategy)({ doc: event, ignoreFields }); - const rule = applyOverrides - ? buildRuleWithOverrides(completeRule, mergedEvent._source ?? {}) - : buildRuleWithoutOverrides(completeRule); - const timestamp = new Date().toISOString(); - const reason = buildReasonMessage({ - name: completeRule.ruleConfig.name, - severity: completeRule.ruleParams.severity, - mergedDoc: mergedEvent, - }); - const signal: Signal = { - ...buildSignal([mergedEvent], rule, reason), - ...additionalSignalFields(mergedEvent), - }; - const eventFields = buildEventTypeSignal(mergedEvent); - // Filter out any kibana.* fields from the generated signal - kibana.* fields are aliases - // in siem-signals so we can't write to them, but for signals-on-signals they'll be returned - // in the fields API response and merged into the mergedDoc source - const { kibana, ...filteredSource } = mergedEvent._source || {}; - // TODO: better naming for SignalHit - it's really a new signal to be inserted - const signalHit: SignalHit = { - ...filteredSource, - [TIMESTAMP]: timestamp, - event: eventFields, - signal, - }; - return signalHit; -}; - -export const objectArrayIntersection = (objects: object[]) => { - if (objects.length === 0) { - return undefined; - } else if (objects.length === 1) { - return objects[0]; - } else { - return objects - .slice(1) - .reduce( - (acc: object | undefined, obj): object | undefined => objectPairIntersection(acc, obj), - objects[0] - ); - } -}; - -export const objectPairIntersection = (a: object | undefined, b: object | undefined) => { - if (a === undefined || b === undefined) { - return undefined; - } - const intersection: Record = {}; - Object.entries(a).forEach(([key, aVal]) => { - if (key in b) { - const bVal = (b as Record)[key]; - if ( - typeof aVal === 'object' && - !(aVal instanceof Array) && - aVal !== null && - typeof bVal === 'object' && - !(bVal instanceof Array) && - bVal !== null - ) { - intersection[key] = objectPairIntersection(aVal, bVal); - } else if (aVal === bVal) { - intersection[key] = aVal; - } - } - }); - // Count up the number of entries that are NOT undefined in the intersection - // If there are no keys OR all entries are undefined, return undefined - if ( - Object.values(intersection).reduce( - (acc: number, value) => (value !== undefined ? acc + 1 : acc), - 0 - ) === 0 - ) { - return undefined; - } else { - return intersection; - } -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_event_type_signal.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_event_type_signal.test.ts deleted file mode 100644 index cc3456e7ab968..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_event_type_signal.test.ts +++ /dev/null @@ -1,101 +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 { sampleDocNoSortId } from './__mocks__/es_results'; -import { buildEventTypeSignal, isEventTypeSignal } from './build_event_type_signal'; - -describe('buildEventTypeSignal', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - test('it returns the event appended of kind signal if it does not exist', () => { - const doc = sampleDocNoSortId(); - delete doc._source.event; - const eventType = buildEventTypeSignal(doc); - const expected: object = { kind: 'signal' }; - expect(eventType).toEqual(expected); - }); - - test('it returns the event appended of kind signal if it is an empty object', () => { - const doc = sampleDocNoSortId(); - doc._source.event = {}; - const eventType = buildEventTypeSignal(doc); - const expected: object = { kind: 'signal' }; - expect(eventType).toEqual(expected); - }); - - test('it returns the event with kind signal and other properties if they exist', () => { - const doc = sampleDocNoSortId(); - doc._source.event = { - action: 'socket_opened', - module: 'system', - dataset: 'socket', - }; - const eventType = buildEventTypeSignal(doc); - const expected: object = { - action: 'socket_opened', - module: 'system', - dataset: 'socket', - kind: 'signal', - }; - expect(eventType).toEqual(expected); - }); - - test('It validates a sample doc with no signal type as "false"', () => { - const doc = sampleDocNoSortId(); - expect(isEventTypeSignal(doc)).toEqual(false); - }); - - test('It validates a sample doc with a signal type as "true"', () => { - const doc = { - ...sampleDocNoSortId(), - _source: { - ...sampleDocNoSortId()._source, - signal: { - rule: { id: 'id-123' }, - }, - }, - }; - expect(isEventTypeSignal(doc)).toEqual(true); - }); - - test('It validates a numeric signal string as "false"', () => { - const doc = { - ...sampleDocNoSortId(), - _source: { - ...sampleDocNoSortId()._source, - signal: 'something', - }, - }; - expect(isEventTypeSignal(doc)).toEqual(false); - }); - - test('It validates an empty object as "false"', () => { - const doc = { - ...sampleDocNoSortId(), - _source: { - ...sampleDocNoSortId()._source, - signal: {}, - }, - }; - expect(isEventTypeSignal(doc)).toEqual(false); - }); - - test('It validates an empty rule object as "false"', () => { - const doc = { - ...sampleDocNoSortId(), - _source: { - ...sampleDocNoSortId()._source, - signal: { - rule: {}, - }, - }, - }; - expect(isEventTypeSignal(doc)).toEqual(false); - }); -}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_event_type_signal.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_event_type_signal.ts deleted file mode 100644 index 0dd2acfb88ffe..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_event_type_signal.ts +++ /dev/null @@ -1,30 +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 { BaseSignalHit, SimpleHit } from './types'; -import { getField } from './utils'; - -export const buildEventTypeSignal = (doc: BaseSignalHit): object => { - if (doc._source != null && doc._source.event instanceof Object) { - return { ...doc._source.event, kind: 'signal' }; - } else { - return { kind: 'signal' }; - } -}; - -/** - * Given a document this will return true if that document is a signal - * document. We can't guarantee the code will call this function with a document - * before adding the _source.event.kind = "signal" from "buildEventTypeSignal" - * so we do basic testing to ensure that if the object has the fields of: - * "signal.rule.id" then it will be one of our signals rather than a customer - * overwritten signal. - * @param doc The document which might be a signal or it might be a regular log - */ -export const isEventTypeSignal = (doc: SimpleHit): boolean => { - const ruleId = getField(doc, 'signal.rule.id'); - return ruleId != null && typeof ruleId === 'string'; -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.test.ts deleted file mode 100644 index 9ae51688ee676..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.test.ts +++ /dev/null @@ -1,189 +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 { buildRuleWithOverrides, buildRuleWithoutOverrides } from './build_rule'; -import { sampleDocNoSortId, expectedRule, sampleDocSeverity } from './__mocks__/es_results'; -import { RulesSchema } from '../../../../common/detection_engine/schemas/response/rules_schema'; -import { INTERNAL_RULE_ID_KEY, INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants'; -import { - getCompleteRuleMock, - getQueryRuleParams, - getThreatRuleParams, -} from '../schemas/rule_schemas.mock'; -import { - CompleteRule, - QueryRuleParams, - RuleParams, - ThreatRuleParams, -} from '../schemas/rule_schemas'; - -describe('buildRuleWithoutOverrides', () => { - let params: RuleParams; - let completeRule: CompleteRule; - - beforeEach(() => { - params = getQueryRuleParams(); - completeRule = getCompleteRuleMock(params); - }); - - test('builds a rule using rule alert', () => { - const rule = buildRuleWithoutOverrides(completeRule); - expect(rule).toEqual(expectedRule()); - }); - - test('builds a rule and removes internal tags', () => { - completeRule.ruleConfig.tags = [ - 'some fake tag 1', - 'some fake tag 2', - `${INTERNAL_RULE_ID_KEY}:rule-1`, - `${INTERNAL_IMMUTABLE_KEY}:true`, - ]; - const rule = buildRuleWithoutOverrides(completeRule); - expect(rule.tags).toEqual(['some fake tag 1', 'some fake tag 2']); - }); - - test('it builds a rule as expected with filters present', () => { - const ruleFilters = [ - { - query: 'host.name: Rebecca', - }, - { - query: 'host.name: Evan', - }, - { - query: 'host.name: Braden', - }, - ]; - completeRule.ruleParams.filters = ruleFilters; - const rule = buildRuleWithoutOverrides(completeRule); - expect(rule.filters).toEqual(ruleFilters); - }); - - test('it creates a indicator/threat_mapping/threat_matching rule', () => { - const ruleParams: ThreatRuleParams = { - ...getThreatRuleParams(), - threatMapping: [ - { - entries: [ - { - field: 'host.name', - value: 'host.name', - type: 'mapping', - }, - ], - }, - ], - threatFilters: [ - { - query: { - bool: { - must: [ - { - query_string: { - query: 'host.name: linux', - analyze_wildcard: true, - time_zone: 'Zulu', - }, - }, - ], - }, - }, - }, - ], - threatIndicatorPath: 'some.path', - threatQuery: 'threat_query', - threatIndex: ['threat_index'], - threatLanguage: 'kuery', - }; - const threatMatchCompleteRule = getCompleteRuleMock(ruleParams); - const threatMatchRule = buildRuleWithoutOverrides(threatMatchCompleteRule); - const expected: Partial = { - threat_mapping: ruleParams.threatMapping, - threat_filters: ruleParams.threatFilters, - threat_indicator_path: ruleParams.threatIndicatorPath, - threat_query: ruleParams.threatQuery, - threat_index: ruleParams.threatIndex, - threat_language: ruleParams.threatLanguage, - }; - expect(threatMatchRule).toEqual(expect.objectContaining(expected)); - }); -}); - -describe('buildRuleWithOverrides', () => { - let params: RuleParams; - let completeRule: CompleteRule; - - beforeEach(() => { - params = getQueryRuleParams(); - completeRule = getCompleteRuleMock(params); - }); - - test('it applies rule name override in buildRule', () => { - completeRule.ruleParams.ruleNameOverride = 'someKey'; - const rule = buildRuleWithOverrides(completeRule, sampleDocNoSortId()._source!); - const expected = { - ...expectedRule(), - name: 'someValue', - rule_name_override: 'someKey', - meta: { - ruleNameOverridden: true, - someMeta: 'someField', - }, - }; - expect(rule).toEqual(expected); - }); - - test('it applies risk score override in buildRule', () => { - const newRiskScore = 79; - completeRule.ruleParams.riskScoreMapping = [ - { - field: 'new_risk_score', - // value and risk_score aren't used for anything but are required in the schema - value: '', - operator: 'equals', - risk_score: undefined, - }, - ]; - const doc = sampleDocNoSortId(); - doc._source.new_risk_score = newRiskScore; - const rule = buildRuleWithOverrides(completeRule, doc._source!); - const expected = { - ...expectedRule(), - risk_score: newRiskScore, - risk_score_mapping: completeRule.ruleParams.riskScoreMapping, - meta: { - riskScoreOverridden: true, - someMeta: 'someField', - }, - }; - expect(rule).toEqual(expected); - }); - - test('it applies severity override in buildRule', () => { - const eventSeverity = '42'; - completeRule.ruleParams.severityMapping = [ - { - field: 'event.severity', - value: eventSeverity, - operator: 'equals', - severity: 'critical', - }, - ]; - const doc = sampleDocSeverity(Number(eventSeverity)); - const rule = buildRuleWithOverrides(completeRule, doc._source!); - const expected = { - ...expectedRule(), - severity: 'critical', - severity_mapping: completeRule.ruleParams.severityMapping, - meta: { - severityOverrideField: 'event.severity', - someMeta: 'someField', - }, - }; - expect(rule).toEqual(expected); - }); -}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.ts deleted file mode 100644 index ab40ce330370c..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.ts +++ /dev/null @@ -1,91 +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 { RulesSchema } from '../../../../common/detection_engine/schemas/response/rules_schema'; -import { buildRiskScoreFromMapping } from './mappings/build_risk_score_from_mapping'; -import { SignalSource } from './types'; -import { buildSeverityFromMapping } from './mappings/build_severity_from_mapping'; -import { buildRuleNameFromMapping } from './mappings/build_rule_name_from_mapping'; -import { CompleteRule, RuleParams } from '../schemas/rule_schemas'; -import { commonParamsCamelToSnake, typeSpecificCamelToSnake } from '../schemas/rule_converters'; -import { transformTags } from '../routes/rules/utils'; -import { transformAlertToRuleAction } from '../../../../common/detection_engine/transform_actions'; - -export const buildRuleWithoutOverrides = (completeRule: CompleteRule): RulesSchema => { - const ruleParams = completeRule.ruleParams; - const { - actions, - schedule, - name, - tags, - enabled, - createdBy, - updatedBy, - throttle, - createdAt, - updatedAt, - } = completeRule.ruleConfig; - return { - actions: actions.map(transformAlertToRuleAction), - created_at: createdAt.toISOString(), - created_by: createdBy ?? '', - enabled, - id: completeRule.alertId, - interval: schedule.interval, - name, - tags: transformTags(tags), - throttle: throttle ?? undefined, - updated_at: updatedAt.toISOString(), - updated_by: updatedBy ?? '', - ...commonParamsCamelToSnake(ruleParams), - ...typeSpecificCamelToSnake(ruleParams), - }; -}; - -export const buildRuleWithOverrides = ( - completeRule: CompleteRule, - eventSource: SignalSource -): RulesSchema => { - const ruleWithoutOverrides = buildRuleWithoutOverrides(completeRule); - return applyRuleOverrides(ruleWithoutOverrides, eventSource, completeRule.ruleParams); -}; - -export const applyRuleOverrides = ( - rule: RulesSchema, - eventSource: SignalSource, - ruleParams: RuleParams -): RulesSchema => { - const { riskScore, riskScoreMeta } = buildRiskScoreFromMapping({ - eventSource, - riskScore: ruleParams.riskScore, - riskScoreMapping: ruleParams.riskScoreMapping, - }); - - const { severity, severityMeta } = buildSeverityFromMapping({ - eventSource, - severity: ruleParams.severity, - severityMapping: ruleParams.severityMapping, - }); - - const { ruleName, ruleNameMeta } = buildRuleNameFromMapping({ - eventSource, - ruleName: rule.name, - ruleNameMapping: ruleParams.ruleNameOverride, - }); - - const meta = { ...ruleParams.meta, ...riskScoreMeta, ...severityMeta, ...ruleNameMeta }; - return { - ...rule, - risk_score: riskScore, - risk_score_mapping: ruleParams.riskScoreMapping ?? [], - severity, - severity_mapping: ruleParams.severityMapping ?? [], - name: ruleName, - rule_name_override: ruleParams.ruleNameOverride, - meta: Object.keys(meta).length > 0 ? meta : undefined, - }; -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_signal.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_signal.test.ts deleted file mode 100644 index e06e8a5cdcf76..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_signal.test.ts +++ /dev/null @@ -1,376 +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 { sampleDocNoSortId } from './__mocks__/es_results'; -import { - buildSignal, - buildParent, - buildAncestors, - additionalSignalFields, - removeClashes, -} from './build_signal'; -import { Signal, Ancestor, BaseSignalHit } from './types'; -import { - getRulesSchemaMock, - ANCHOR_DATE, -} from '../../../../common/detection_engine/schemas/response/rules_schema.mocks'; -import { getListArrayMock } from '../../../../common/detection_engine/schemas/types/lists.mock'; -import { SIGNALS_TEMPLATE_VERSION } from '../routes/index/get_signals_template'; - -describe('buildSignal', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - test('it builds a signal as expected without original_event if event does not exist', () => { - const doc = sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); - delete doc._source.event; - const rule = getRulesSchemaMock(); - const reason = 'signal reasonable reason'; - - const signal = { - ...buildSignal([doc], rule, reason), - ...additionalSignalFields(doc), - }; - const expected: Signal = { - _meta: { - version: SIGNALS_TEMPLATE_VERSION, - }, - parent: { - id: 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71', - type: 'event', - index: 'myFakeSignalIndex', - depth: 0, - }, - parents: [ - { - id: 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71', - type: 'event', - index: 'myFakeSignalIndex', - depth: 0, - }, - ], - ancestors: [ - { - id: 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71', - type: 'event', - index: 'myFakeSignalIndex', - depth: 0, - }, - ], - original_time: '2020-04-20T21:27:45.000Z', - reason: 'signal reasonable reason', - status: 'open', - rule: { - author: [], - id: '7a7065d7-6e8b-4aae-8d20-c93613dec9f9', - created_at: new Date(ANCHOR_DATE).toISOString(), - updated_at: new Date(ANCHOR_DATE).toISOString(), - created_by: 'elastic', - description: 'some description', - enabled: true, - false_positives: ['false positive 1', 'false positive 2'], - from: 'now-6m', - immutable: false, - name: 'Query with a rule id', - query: 'user.name: root or user.name: admin', - references: ['test 1', 'test 2'], - severity: 'high', - severity_mapping: [], - updated_by: 'elastic_kibana', - tags: ['some fake tag 1', 'some fake tag 2'], - to: 'now', - type: 'query', - threat: [], - version: 1, - output_index: '.siem-signals-default', - max_signals: 100, - risk_score: 55, - risk_score_mapping: [], - language: 'kuery', - rule_id: 'query-rule-id', - interval: '5m', - exceptions_list: getListArrayMock(), - }, - depth: 1, - }; - expect(signal).toEqual(expected); - }); - - test('it builds a signal as expected with original_event if is present', () => { - const doc = sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); - doc._source.event = { - action: 'socket_opened', - dataset: 'socket', - kind: 'event', - module: 'system', - }; - const rule = getRulesSchemaMock(); - const reason = 'signal reasonable reason'; - const signal = { - ...buildSignal([doc], rule, reason), - ...additionalSignalFields(doc), - }; - const expected: Signal = { - _meta: { - version: SIGNALS_TEMPLATE_VERSION, - }, - parent: { - id: 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71', - type: 'event', - index: 'myFakeSignalIndex', - depth: 0, - }, - parents: [ - { - id: 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71', - type: 'event', - index: 'myFakeSignalIndex', - depth: 0, - }, - ], - ancestors: [ - { - id: 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71', - type: 'event', - index: 'myFakeSignalIndex', - depth: 0, - }, - ], - original_time: '2020-04-20T21:27:45.000Z', - reason: 'signal reasonable reason', - original_event: { - action: 'socket_opened', - dataset: 'socket', - kind: 'event', - module: 'system', - }, - status: 'open', - rule: { - author: [], - id: '7a7065d7-6e8b-4aae-8d20-c93613dec9f9', - created_at: new Date(ANCHOR_DATE).toISOString(), - updated_at: new Date(ANCHOR_DATE).toISOString(), - created_by: 'elastic', - description: 'some description', - enabled: true, - false_positives: ['false positive 1', 'false positive 2'], - from: 'now-6m', - immutable: false, - name: 'Query with a rule id', - query: 'user.name: root or user.name: admin', - references: ['test 1', 'test 2'], - severity: 'high', - severity_mapping: [], - updated_by: 'elastic_kibana', - tags: ['some fake tag 1', 'some fake tag 2'], - to: 'now', - type: 'query', - threat: [], - version: 1, - output_index: '.siem-signals-default', - max_signals: 100, - risk_score: 55, - risk_score_mapping: [], - language: 'kuery', - rule_id: 'query-rule-id', - interval: '5m', - exceptions_list: getListArrayMock(), - }, - depth: 1, - }; - expect(signal).toEqual(expected); - }); - - test('it builds a ancestor correctly if the parent does not exist', () => { - const doc = sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); - doc._source.event = { - action: 'socket_opened', - dataset: 'socket', - kind: 'event', - module: 'system', - }; - const signal = buildParent(doc); - const expected: Ancestor = { - id: 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71', - type: 'event', - index: 'myFakeSignalIndex', - depth: 0, - }; - expect(signal).toEqual(expected); - }); - - test('it builds a ancestor correctly if the parent does exist', () => { - const doc = sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); - doc._source.event = { - action: 'socket_opened', - dataset: 'socket', - kind: 'event', - module: 'system', - }; - doc._source.signal = { - parents: [ - { - id: '730ddf9e-5a00-4f85-9ddf-5878ca511a87', - type: 'event', - index: 'myFakeSignalIndex', - depth: 0, - }, - ], - ancestors: [ - { - id: '730ddf9e-5a00-4f85-9ddf-5878ca511a87', - type: 'event', - index: 'myFakeSignalIndex', - depth: 0, - }, - ], - depth: 1, - rule: { - id: '98c0bf9e-4d38-46f4-9a6a-8a820426256b', - }, - }; - const signal = buildParent(doc); - const expected: Ancestor = { - rule: '98c0bf9e-4d38-46f4-9a6a-8a820426256b', - id: 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71', - type: 'signal', - index: 'myFakeSignalIndex', - depth: 1, - }; - expect(signal).toEqual(expected); - }); - - test('it builds a signal ancestor correctly if the parent does not exist', () => { - const doc = sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); - doc._source.event = { - action: 'socket_opened', - dataset: 'socket', - kind: 'event', - module: 'system', - }; - const signal = buildAncestors(doc); - const expected: Ancestor[] = [ - { - id: 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71', - type: 'event', - index: 'myFakeSignalIndex', - depth: 0, - }, - ]; - expect(signal).toEqual(expected); - }); - - test('it builds a signal ancestor correctly if the parent does exist', () => { - const doc = sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); - doc._source.event = { - action: 'socket_opened', - dataset: 'socket', - kind: 'event', - module: 'system', - }; - doc._source.signal = { - parents: [ - { - id: '730ddf9e-5a00-4f85-9ddf-5878ca511a87', - type: 'event', - index: 'myFakeSignalIndex', - depth: 0, - }, - ], - ancestors: [ - { - id: '730ddf9e-5a00-4f85-9ddf-5878ca511a87', - type: 'event', - index: 'myFakeSignalIndex', - depth: 0, - }, - ], - rule: { - id: '98c0bf9e-4d38-46f4-9a6a-8a820426256b', - }, - depth: 1, - }; - const signal = buildAncestors(doc); - const expected: Ancestor[] = [ - { - id: '730ddf9e-5a00-4f85-9ddf-5878ca511a87', - type: 'event', - index: 'myFakeSignalIndex', - depth: 0, - }, - { - rule: '98c0bf9e-4d38-46f4-9a6a-8a820426256b', - id: 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71', - type: 'signal', - index: 'myFakeSignalIndex', - depth: 1, - }, - ]; - expect(signal).toEqual(expected); - }); - - describe('removeClashes', () => { - test('it will call renameClashes with a regular doc and not mutate it if it does not have a signal clash', () => { - const doc = sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); - const output = removeClashes(doc); - expect(output).toBe(doc); // reference check - }); - - test('it will call renameClashes with a regular doc and not change anything', () => { - const doc = sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); - const output = removeClashes(doc); - expect(output).toEqual(doc); // deep equal check - }); - - test('it will remove a "signal" numeric clash', () => { - const sampleDoc = sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); - const doc = { - ...sampleDoc, - _source: { - ...sampleDoc._source, - signal: 127, - }, - } as unknown as BaseSignalHit; - const output = removeClashes(doc); - expect(output).toEqual(sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71')); - }); - - test('it will remove a "signal" object clash', () => { - const sampleDoc = sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); - const doc = { - ...sampleDoc, - _source: { - ...sampleDoc._source, - signal: { child_1: { child_2: 'Test nesting' } }, - }, - } as unknown as BaseSignalHit; - const output = removeClashes(doc); - expect(output).toEqual(sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71')); - }); - - test('it will not remove a "signal" if that is signal is one of our signals', () => { - const sampleDoc = sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); - const doc = { - ...sampleDoc, - _source: { - ...sampleDoc._source, - signal: { rule: { id: '123' } }, - }, - } as unknown as BaseSignalHit; - const output = removeClashes(doc); - const expected = { - ...sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'), - _source: { - ...sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71')._source, - signal: { rule: { id: '123' } }, - }, - }; - expect(output).toEqual(expected); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_signal.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_signal.ts deleted file mode 100644 index 5e26466557217..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_signal.ts +++ /dev/null @@ -1,130 +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 { SearchTypes } from '../../../../common/detection_engine/types'; -import { RulesSchema } from '../../../../common/detection_engine/schemas/response/rules_schema'; -import { SIGNALS_TEMPLATE_VERSION } from '../routes/index/get_signals_template'; -import { isEventTypeSignal } from './build_event_type_signal'; -import { Signal, Ancestor, BaseSignalHit, ThresholdResult, SimpleHit } from './types'; -import { getValidDateFromDoc } from './utils'; - -/** - * Takes a parent signal or event document and extracts the information needed for the corresponding entry in the child - * signal's `signal.parents` array. - * @param doc The parent signal or event - */ -export const buildParent = (doc: BaseSignalHit): Ancestor => { - if (doc._source?.signal != null) { - return { - rule: doc._source?.signal.rule.id, - id: doc._id, - type: 'signal', - index: doc._index, - // We first look for signal.depth and use that if it exists. If it doesn't exist, this should be a pre-7.10 signal - // and should have signal.parent.depth instead. signal.parent.depth in this case is treated as equivalent to signal.depth. - depth: doc._source?.signal.depth ?? doc._source?.signal.parent?.depth ?? 1, - }; - } else { - return { - id: doc._id, - type: 'event', - index: doc._index, - depth: 0, - }; - } -}; - -/** - * Takes a parent signal or event document with N ancestors and adds the parent document to the ancestry array, - * creating an array of N+1 ancestors. - * @param doc The parent signal/event for which to extend the ancestry. - */ -export const buildAncestors = (doc: BaseSignalHit): Ancestor[] => { - const newAncestor = buildParent(doc); - const existingAncestors = doc._source?.signal?.ancestors; - if (existingAncestors != null) { - return [...existingAncestors, newAncestor]; - } else { - return [newAncestor]; - } -}; - -/** - * This removes any signal named clashes such as if a source index has - * "signal" but is not a signal object we put onto the object. If this - * is our "signal object" then we don't want to remove it. - * @param doc The source index doc to a signal. - */ -export const removeClashes = (doc: BaseSignalHit): BaseSignalHit => { - // @ts-expect-error @elastic/elasticsearch _source is optional - const { signal, ...noSignal } = doc._source; - if (signal == null || isEventTypeSignal(doc as SimpleHit)) { - return doc; - } else { - return { - ...doc, - _source: { ...noSignal }, - }; - } -}; - -/** - * Builds the `signal.*` fields that are common across all signals. - * @param docs The parent signals/events of the new signal to be built. - * @param rule The rule that is generating the new signal. - */ -export const buildSignal = (docs: BaseSignalHit[], rule: RulesSchema, reason: string): Signal => { - const _meta = { - version: SIGNALS_TEMPLATE_VERSION, - }; - const removedClashes = docs.map(removeClashes); - const parents = removedClashes.map(buildParent); - const depth = parents.reduce((acc, parent) => Math.max(parent.depth, acc), 0) + 1; - const ancestors = removedClashes.reduce( - (acc: Ancestor[], doc) => acc.concat(buildAncestors(doc)), - [] - ); - return { - _meta, - parents, - ancestors, - status: 'open', - rule, - reason, - depth, - }; -}; - -const isThresholdResult = (thresholdResult: SearchTypes): thresholdResult is ThresholdResult => { - return typeof thresholdResult === 'object'; -}; - -/** - * Creates signal fields that are only available in the special case where a signal has only 1 parent signal/event. - * We copy the original time from the document as "original_time" since we override the timestamp with the current date time. - * @param doc The parent signal/event of the new signal to be built. - */ -export const additionalSignalFields = (doc: BaseSignalHit) => { - const thresholdResult = doc._source?.threshold_result; - if (thresholdResult != null && !isThresholdResult(thresholdResult)) { - throw new Error(`threshold_result failed to validate: ${thresholdResult}`); - } - const originalTime = getValidDateFromDoc({ - doc, - timestampOverride: undefined, - }); - return { - parent: buildParent(removeClashes(doc)), - original_time: originalTime != null ? originalTime.toISOString() : undefined, - original_event: doc._source?.event ?? undefined, - threshold_result: thresholdResult, - original_signal: - doc._source?.signal != null && !isEventTypeSignal(doc as SimpleHit) - ? doc._source?.signal - : undefined, - }; -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_factory.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_factory.ts deleted file mode 100644 index a8334cf0a4396..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_factory.ts +++ /dev/null @@ -1,107 +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 { performance } from 'perf_hooks'; -import { countBy, isEmpty, get } from 'lodash'; - -import { ElasticsearchClient, Logger } from 'kibana/server'; -import { BuildRuleMessage } from './rule_messages'; -import { RefreshTypes } from '../types'; -import { BaseHit } from '../../../../common/detection_engine/types'; -import { errorAggregator, makeFloatString } from './utils'; -import { withSecuritySpan } from '../../../utils/with_security_span'; - -export interface GenericBulkCreateResponse { - success: boolean; - bulkCreateDuration: string; - createdItemsCount: number; - createdItems: Array; - errors: string[]; -} - -export const bulkCreateFactory = - ( - logger: Logger, - esClient: ElasticsearchClient, - buildRuleMessage: BuildRuleMessage, - refreshForBulkCreate: RefreshTypes, - indexNameOverride?: string - ) => - async (wrappedDocs: Array>): Promise> => { - if (wrappedDocs.length === 0) { - return { - errors: [], - success: true, - bulkCreateDuration: '0', - createdItemsCount: 0, - createdItems: [], - }; - } - - const bulkBody = wrappedDocs.flatMap((wrappedDoc) => [ - { - create: { - _index: indexNameOverride ?? wrappedDoc._index, - _id: wrappedDoc._id, - }, - }, - wrappedDoc._source, - ]); - const start = performance.now(); - - const response = await withSecuritySpan('writeAlertsBulk', () => - esClient.bulk({ - refresh: refreshForBulkCreate, - body: bulkBody, - }) - ); - - const end = performance.now(); - logger.debug( - buildRuleMessage( - `individual bulk process time took: ${makeFloatString(end - start)} milliseconds` - ) - ); - logger.debug(buildRuleMessage(`took property says bulk took: ${response.took} milliseconds`)); - const createdItems = wrappedDocs - .map((doc, index) => ({ - _id: response.items[index].create?._id ?? '', - _index: response.items[index].create?._index ?? '', - ...doc._source, - })) - .filter((_, index) => get(response.items[index], 'create.status') === 201); - const createdItemsCount = createdItems.length; - const duplicateSignalsCount = countBy(response.items, 'create.status')['409']; - const errorCountByMessage = errorAggregator(response, [409]); - - logger.debug(buildRuleMessage(`bulk created ${createdItemsCount} signals`)); - if (duplicateSignalsCount > 0) { - logger.debug(buildRuleMessage(`ignored ${duplicateSignalsCount} duplicate signals`)); - } - if (!isEmpty(errorCountByMessage)) { - logger.error( - buildRuleMessage( - `[-] bulkResponse had errors with responses of: ${JSON.stringify(errorCountByMessage)}` - ) - ); - return { - errors: Object.keys(errorCountByMessage), - success: false, - bulkCreateDuration: makeFloatString(end - start), - createdItemsCount, - createdItems, - }; - } else { - return { - errors: [], - success: true, - bulkCreateDuration: makeFloatString(end - start), - createdItemsCount, - createdItems, - }; - } - }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts index 2453e92dc3c0a..a757e178ea48a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts @@ -14,7 +14,7 @@ import { AlertInstanceState, AlertServices, } from '../../../../../alerting/server'; -import { GenericBulkCreateResponse } from './bulk_create_factory'; +import { GenericBulkCreateResponse } from '../rule_types/factories'; import { AnomalyResults, Anomaly } from '../../machine_learning'; import { BuildRuleMessage } from './rule_messages'; import { BulkCreate, WrapHits } from './types'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts index 120bf2c2ebfce..5f1ab1c2dd5ff 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts @@ -84,7 +84,6 @@ export const queryExecutor = async ({ eventsTelemetry, id: completeRule.alertId, inputIndexPattern: inputIndex, - signalsIndex: ruleParams.outputIndex, filter: esFilter, pageSize: searchAfterSize, buildReasonMessage: buildReasonMessageForQueryAlert, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_filter.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_filter.ts index f849900ec75e1..f113e84c88ba8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_filter.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_filter.ts @@ -21,8 +21,8 @@ import { AlertServices, } from '../../../../../alerting/server'; import { PartialFilter } from '../types'; -import { QueryFilter } from './types'; import { withSecuritySpan } from '../../../utils/with_security_span'; +import { ESBoolQuery } from '../../../../common/typed_json'; interface GetFilterArgs { type: Type; @@ -53,7 +53,7 @@ export const getFilter = async ({ type, query, lists, -}: GetFilterArgs): Promise => { +}: GetFilterArgs): Promise => { const queryFilter = () => { if (query != null && language != null && index != null) { return getQueryFilter(query, language, filters || [], index, lists); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts index 7d22d58efdd6f..52d0a04eee1ec 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts @@ -15,7 +15,6 @@ import { sampleDocWithSortId, } from './__mocks__/es_results'; import { searchAfterAndBulkCreate } from './search_after_bulk_create'; -import { DEFAULT_SIGNALS_INDEX } from '../../../../common/constants'; import { alertsMock, AlertServicesMock } from '../../../../../alerting/server/mocks'; import uuid from 'uuid'; import { listMock } from '../../../../../lists/server/mocks'; @@ -27,17 +26,19 @@ import { getRuleRangeTuples } from './utils'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mocks'; import { getCompleteRuleMock, getQueryRuleParams } from '../schemas/rule_schemas.mock'; -import { bulkCreateFactory } from './bulk_create_factory'; -import { wrapHitsFactory } from './wrap_hits_factory'; +import { bulkCreateFactory } from '../rule_types/factories/bulk_create_factory'; +import { wrapHitsFactory } from '../rule_types/factories/wrap_hits_factory'; import { mockBuildRuleMessage } from './__mocks__/build_rule_message.mock'; -import { errors as esErrors } from '@elastic/elasticsearch'; import { BuildReasonMessage } from './reason_formatters'; import { QueryRuleParams } from '../schemas/rule_schemas'; +import { createPersistenceServicesMock } from '../../../../../rule_registry/server/utils/create_persistence_rule_type_wrapper.mock'; +import { PersistenceServices } from '../../../../../rule_registry/server'; const buildRuleMessage = mockBuildRuleMessage; describe('searchAfterAndBulkCreate', () => { let mockService: AlertServicesMock; + let mockPersistenceServices: jest.Mocked; let buildReasonMessage: BuildReasonMessage; let bulkCreate: BulkCreate; let wrapHits: WrapHits; @@ -46,6 +47,9 @@ describe('searchAfterAndBulkCreate', () => { const someGuids = Array.from({ length: 13 }).map(() => uuid.v4()); const sampleParams = getQueryRuleParams(); const queryCompleteRule = getCompleteRuleMock(sampleParams); + const defaultFilter = { + match_all: {}, + }; sampleParams.maxSignals = 30; let tuple: RuleRangeTuple; beforeEach(() => { @@ -65,17 +69,18 @@ describe('searchAfterAndBulkCreate', () => { maxSignals: sampleParams.maxSignals, buildRuleMessage, }).tuples[0]; + mockPersistenceServices = createPersistenceServicesMock(); bulkCreate = bulkCreateFactory( mockLogger, - mockService.scopedClusterClient.asCurrentUser, + mockPersistenceServices.alertWithPersistence, buildRuleMessage, false ); wrapHits = wrapHitsFactory({ completeRule: queryCompleteRule, - signalsIndex: DEFAULT_SIGNALS_INDEX, mergeStrategy: 'missingFields', ignoreFields: [], + spaceId: 'default', }); }); @@ -86,17 +91,9 @@ describe('searchAfterAndBulkCreate', () => { ) ); - mockService.scopedClusterClient.asCurrentUser.bulk.mockResponseOnce({ - took: 100, - errors: false, - items: [ - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, - ], + mockPersistenceServices.alertWithPersistence.mockResolvedValueOnce({ + createdAlerts: [{ _id: '1', _index: '.internal.alerts-security.alerts-default-000001' }], + errors: {}, }); mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( @@ -105,17 +102,9 @@ describe('searchAfterAndBulkCreate', () => { ) ); - mockService.scopedClusterClient.asCurrentUser.bulk.mockResponseOnce({ - took: 100, - errors: false, - items: [ - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, - ], + mockPersistenceServices.alertWithPersistence.mockResolvedValueOnce({ + createdAlerts: [{ _id: '2', _index: '.internal.alerts-security.alerts-default-000001' }], + errors: {}, }); mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( @@ -124,17 +113,9 @@ describe('searchAfterAndBulkCreate', () => { ) ); - mockService.scopedClusterClient.asCurrentUser.bulk.mockResponseOnce({ - took: 100, - errors: false, - items: [ - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, - ], + mockPersistenceServices.alertWithPersistence.mockResolvedValueOnce({ + createdAlerts: [{ _id: '3', _index: '.internal.alerts-security.alerts-default-000001' }], + errors: {}, }); mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( @@ -143,17 +124,9 @@ describe('searchAfterAndBulkCreate', () => { ) ); - mockService.scopedClusterClient.asCurrentUser.bulk.mockResponseOnce({ - took: 100, - errors: false, - items: [ - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, - ], + mockPersistenceServices.alertWithPersistence.mockResolvedValueOnce({ + createdAlerts: [{ _id: '4', _index: '.internal.alerts-security.alerts-default-000001' }], + errors: {}, }); mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( @@ -185,9 +158,8 @@ describe('searchAfterAndBulkCreate', () => { eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, - signalsIndex: DEFAULT_SIGNALS_INDEX, pageSize: 1, - filter: undefined, + filter: defaultFilter, buildReasonMessage, buildRuleMessage, bulkCreate, @@ -205,17 +177,9 @@ describe('searchAfterAndBulkCreate', () => { repeatedSearchResultsWithSortId(4, 1, someGuids.slice(0, 3)) ) ); - mockService.scopedClusterClient.asCurrentUser.bulk.mockResponseOnce({ - took: 100, - errors: false, - items: [ - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, - ], + mockPersistenceServices.alertWithPersistence.mockResolvedValueOnce({ + createdAlerts: [{ _id: '1', _index: '.internal.alerts-security.alerts-default-000001' }], + errors: {}, }); mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( @@ -224,17 +188,9 @@ describe('searchAfterAndBulkCreate', () => { ) ); - mockService.scopedClusterClient.asCurrentUser.bulk.mockResponseOnce({ - took: 100, - errors: false, - items: [ - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, - ], + mockPersistenceServices.alertWithPersistence.mockResolvedValueOnce({ + createdAlerts: [{ _id: '2', _index: '.internal.alerts-security.alerts-default-000001' }], + errors: {}, }); mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( @@ -243,17 +199,9 @@ describe('searchAfterAndBulkCreate', () => { ) ); - mockService.scopedClusterClient.asCurrentUser.bulk.mockResponseOnce({ - took: 100, - errors: false, - items: [ - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, - ], + mockPersistenceServices.alertWithPersistence.mockResolvedValueOnce({ + createdAlerts: [{ _id: '3', _index: '.internal.alerts-security.alerts-default-000001' }], + errors: {}, }); mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( @@ -284,9 +232,8 @@ describe('searchAfterAndBulkCreate', () => { eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, - signalsIndex: DEFAULT_SIGNALS_INDEX, pageSize: 1, - filter: undefined, + filter: defaultFilter, buildReasonMessage, buildRuleMessage, bulkCreate, @@ -305,35 +252,14 @@ describe('searchAfterAndBulkCreate', () => { ) ); - mockService.scopedClusterClient.asCurrentUser.bulk.mockResponseOnce({ - took: 100, - errors: false, - items: [ - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, + mockPersistenceServices.alertWithPersistence.mockResolvedValueOnce({ + createdAlerts: [ + { _id: '1', _index: '.internal.alerts-security.alerts-default-000001' }, + { _id: '2', _index: '.internal.alerts-security.alerts-default-000001' }, + { _id: '3', _index: '.internal.alerts-security.alerts-default-000001' }, + { _id: '4', _index: '.internal.alerts-security.alerts-default-000001' }, ], + errors: {}, }); mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( @@ -364,9 +290,8 @@ describe('searchAfterAndBulkCreate', () => { eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, - signalsIndex: DEFAULT_SIGNALS_INDEX, pageSize: 1, - filter: undefined, + filter: defaultFilter, buildReasonMessage, buildRuleMessage, bulkCreate, @@ -424,9 +349,8 @@ describe('searchAfterAndBulkCreate', () => { eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, - signalsIndex: DEFAULT_SIGNALS_INDEX, pageSize: 1, - filter: undefined, + filter: defaultFilter, buildReasonMessage, buildRuleMessage, bulkCreate, @@ -439,39 +363,14 @@ describe('searchAfterAndBulkCreate', () => { }); test('should return success when empty string sortId present', async () => { - mockService.scopedClusterClient.asCurrentUser.bulk.mockResponseOnce({ - took: 100, - errors: false, - items: [ - { - create: { - _id: someGuids[0], - _index: 'myfakeindex', - status: 201, - }, - }, - { - create: { - _id: someGuids[1], - _index: 'myfakeindex', - status: 201, - }, - }, - { - create: { - _id: someGuids[2], - _index: 'myfakeindex', - status: 201, - }, - }, - { - create: { - _id: someGuids[3], - _index: 'myfakeindex', - status: 201, - }, - }, + mockPersistenceServices.alertWithPersistence.mockResolvedValueOnce({ + createdAlerts: [ + { _id: '1', _index: '.internal.alerts-security.alerts-default-000001' }, + { _id: '2', _index: '.internal.alerts-security.alerts-default-000001' }, + { _id: '3', _index: '.internal.alerts-security.alerts-default-000001' }, + { _id: '4', _index: '.internal.alerts-security.alerts-default-000001' }, ], + errors: {}, }); mockService.scopedClusterClient.asCurrentUser.search .mockResolvedValueOnce( @@ -502,9 +401,8 @@ describe('searchAfterAndBulkCreate', () => { eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, - signalsIndex: DEFAULT_SIGNALS_INDEX, pageSize: 1, - filter: undefined, + filter: defaultFilter, buildReasonMessage, buildRuleMessage, bulkCreate, @@ -558,9 +456,8 @@ describe('searchAfterAndBulkCreate', () => { eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, - signalsIndex: DEFAULT_SIGNALS_INDEX, pageSize: 1, - filter: undefined, + filter: defaultFilter, buildReasonMessage, buildRuleMessage, bulkCreate, @@ -579,35 +476,14 @@ describe('searchAfterAndBulkCreate', () => { ) ); - mockService.scopedClusterClient.asCurrentUser.bulk.mockResponseOnce({ - took: 100, - errors: false, - items: [ - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, + mockPersistenceServices.alertWithPersistence.mockResolvedValueOnce({ + createdAlerts: [ + { _id: '1', _index: '.internal.alerts-security.alerts-default-000001' }, + { _id: '2', _index: '.internal.alerts-security.alerts-default-000001' }, + { _id: '3', _index: '.internal.alerts-security.alerts-default-000001' }, + { _id: '4', _index: '.internal.alerts-security.alerts-default-000001' }, ], + errors: {}, }); const exceptionItem = getExceptionListItemSchemaMock(); @@ -632,9 +508,8 @@ describe('searchAfterAndBulkCreate', () => { eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, - signalsIndex: DEFAULT_SIGNALS_INDEX, pageSize: 1, - filter: undefined, + filter: defaultFilter, buildReasonMessage, buildRuleMessage, bulkCreate, @@ -653,35 +528,14 @@ describe('searchAfterAndBulkCreate', () => { ) ); - mockService.scopedClusterClient.asCurrentUser.bulk.mockResponseOnce({ - took: 100, - errors: false, - items: [ - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, + mockPersistenceServices.alertWithPersistence.mockResolvedValueOnce({ + createdAlerts: [ + { _id: '1', _index: '.internal.alerts-security.alerts-default-000001' }, + { _id: '2', _index: '.internal.alerts-security.alerts-default-000001' }, + { _id: '3', _index: '.internal.alerts-security.alerts-default-000001' }, + { _id: '4', _index: '.internal.alerts-security.alerts-default-000001' }, ], + errors: {}, }); mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( @@ -708,9 +562,8 @@ describe('searchAfterAndBulkCreate', () => { eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, - signalsIndex: DEFAULT_SIGNALS_INDEX, pageSize: 1, - filter: undefined, + filter: defaultFilter, buildReasonMessage, buildRuleMessage, bulkCreate, @@ -722,58 +575,6 @@ describe('searchAfterAndBulkCreate', () => { expect(lastLookBackDate).toEqual(new Date('2020-04-20T21:27:45+0000')); }); - test('if unsuccessful first bulk create', async () => { - const exceptionItem = getExceptionListItemSchemaMock(); - exceptionItem.entries = [ - { - field: 'source.ip', - operator: 'included', - type: 'list', - list: { - id: 'ci-badguys.txt', - type: 'ip', - }, - }, - ]; - mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise( - repeatedSearchResultsWithSortId(4, 1, someGuids.slice(0, 3)) - ) - ); - mockService.scopedClusterClient.asCurrentUser.bulk.mockReturnValue( - elasticsearchClientMock.createErrorTransportRequestPromise( - new esErrors.ResponseError( - elasticsearchClientMock.createApiResponse({ - statusCode: 400, - body: { error: { type: 'bulk_error_type' } }, - }) - ) - ) - ); - const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ - listClient, - exceptionsList: [exceptionItem], - tuple, - completeRule: queryCompleteRule, - services: mockService, - logger: mockLogger, - eventsTelemetry: undefined, - id: sampleRuleGuid, - inputIndexPattern, - signalsIndex: DEFAULT_SIGNALS_INDEX, - pageSize: 1, - filter: undefined, - buildReasonMessage, - buildRuleMessage, - bulkCreate, - wrapHits, - }); - expect(mockLogger.error).toHaveBeenCalled(); - expect(success).toEqual(false); - expect(createdSignalsCount).toEqual(0); - expect(lastLookBackDate).toEqual(new Date('2020-04-20T21:27:45+0000')); - }); - test('should return success with 0 total hits', async () => { const exceptionItem = getExceptionListItemSchemaMock(); exceptionItem.entries = [ @@ -808,9 +609,8 @@ describe('searchAfterAndBulkCreate', () => { eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, - signalsIndex: DEFAULT_SIGNALS_INDEX, pageSize: 1, - filter: undefined, + filter: defaultFilter, buildReasonMessage, buildRuleMessage, bulkCreate, @@ -855,9 +655,8 @@ describe('searchAfterAndBulkCreate', () => { eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, - signalsIndex: DEFAULT_SIGNALS_INDEX, pageSize: 1, - filter: undefined, + filter: defaultFilter, buildReasonMessage, buildRuleMessage, bulkCreate, @@ -895,6 +694,16 @@ describe('searchAfterAndBulkCreate', () => { ) ); + mockPersistenceServices.alertWithPersistence.mockResolvedValueOnce({ + createdAlerts: [{ _id: '1', _index: '.internal.alerts-security.alerts-default-000001' }], + errors: { + 'error on creation': { + count: 1, + statusCode: 500, + }, + }, + }); + mockService.scopedClusterClient.asCurrentUser.bulk.mockResponseOnce(bulkItem); // adds the response with errors we are testing mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( @@ -903,17 +712,9 @@ describe('searchAfterAndBulkCreate', () => { ) ); - mockService.scopedClusterClient.asCurrentUser.bulk.mockResponseOnce({ - took: 100, - errors: false, - items: [ - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, - ], + mockPersistenceServices.alertWithPersistence.mockResolvedValueOnce({ + createdAlerts: [{ _id: '2', _index: '.internal.alerts-security.alerts-default-000001' }], + errors: {}, }); mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( @@ -922,17 +723,9 @@ describe('searchAfterAndBulkCreate', () => { ) ); - mockService.scopedClusterClient.asCurrentUser.bulk.mockResponseOnce({ - took: 100, - errors: false, - items: [ - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, - ], + mockPersistenceServices.alertWithPersistence.mockResolvedValueOnce({ + createdAlerts: [{ _id: '3', _index: '.internal.alerts-security.alerts-default-000001' }], + errors: {}, }); mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( @@ -941,17 +734,9 @@ describe('searchAfterAndBulkCreate', () => { ) ); - mockService.scopedClusterClient.asCurrentUser.bulk.mockResponseOnce({ - took: 100, - errors: false, - items: [ - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, - ], + mockPersistenceServices.alertWithPersistence.mockResolvedValueOnce({ + createdAlerts: [{ _id: '4', _index: '.internal.alerts-security.alerts-default-000001' }], + errors: {}, }); mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( @@ -970,9 +755,8 @@ describe('searchAfterAndBulkCreate', () => { eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, - signalsIndex: DEFAULT_SIGNALS_INDEX, pageSize: 1, - filter: undefined, + filter: defaultFilter, buildReasonMessage, buildRuleMessage, bulkCreate, @@ -992,17 +776,9 @@ describe('searchAfterAndBulkCreate', () => { ) ); - mockService.scopedClusterClient.asCurrentUser.bulk.mockResponseOnce({ - took: 100, - errors: false, - items: [ - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, - ], + mockPersistenceServices.alertWithPersistence.mockResolvedValueOnce({ + createdAlerts: [{ _id: '1', _index: '.internal.alerts-security.alerts-default-000001' }], + errors: {}, }); mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( @@ -1011,17 +787,9 @@ describe('searchAfterAndBulkCreate', () => { ) ); - mockService.scopedClusterClient.asCurrentUser.bulk.mockResponseOnce({ - took: 100, - errors: false, - items: [ - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, - ], + mockPersistenceServices.alertWithPersistence.mockResolvedValueOnce({ + createdAlerts: [{ _id: '2', _index: '.internal.alerts-security.alerts-default-000001' }], + errors: {}, }); mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( @@ -1030,17 +798,9 @@ describe('searchAfterAndBulkCreate', () => { ) ); - mockService.scopedClusterClient.asCurrentUser.bulk.mockResponseOnce({ - took: 100, - errors: false, - items: [ - { - // @ts-expect-error not full response interface - create: { - status: 201, - }, - }, - ], + mockPersistenceServices.alertWithPersistence.mockResolvedValueOnce({ + createdAlerts: [{ _id: '3', _index: '.internal.alerts-security.alerts-default-000001' }], + errors: {}, }); mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( @@ -1061,9 +821,8 @@ describe('searchAfterAndBulkCreate', () => { eventsTelemetry: undefined, id: sampleRuleGuid, inputIndexPattern, - signalsIndex: DEFAULT_SIGNALS_INDEX, pageSize: 1, - filter: undefined, + filter: defaultFilter, buildReasonMessage, buildRuleMessage, bulkCreate, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts index 99230627cb6b8..69c001898b217 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts @@ -76,7 +76,6 @@ export const searchAfterAndBulkCreate = async ({ to: tuple.to.toISOString(), services, logger, - // @ts-expect-error please, declare a type explicitly instead of unknown filter, pageSize: Math.ceil(Math.min(tuple.maxSignals, pageSize)), timestampOverride: ruleParams.timestampOverride, @@ -165,7 +164,7 @@ export const searchAfterAndBulkCreate = async ({ success: bulkSuccess, createdSignalsCount: createdCount, createdSignals: createdItems, - bulkCreateTimes: bulkDuration ? [bulkDuration] : undefined, + bulkCreateTimes: [bulkDuration], errors: bulkErrors, }), ]); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_event_signal.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_event_signal.ts index faa14bcbab309..c5d86c9ab460c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_event_signal.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_event_signal.ts @@ -135,7 +135,6 @@ export const createEventSignal = async ({ logger, pageSize: searchAfterSize, services, - signalsIndex: outputIndex, sortOrder: 'desc', trackTotalHits: false, tuple, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts index 220bebbaa4d21..a07de583d8bab 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts @@ -86,7 +86,6 @@ export const createThreatSignal = async ({ logger, pageSize: searchAfterSize, services, - signalsIndex: outputIndex, sortOrder: 'desc', trackTotalHits: false, tuple, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.test.ts index 2c14e4bed62a8..4f68be017ad67 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.test.ts @@ -5,10 +5,8 @@ * 2.0. */ -import { loggingSystemMock } from '../../../../../../../../src/core/server/mocks'; import { ThresholdNormalized } from '../../../../../common/detection_engine/schemas/common/schemas'; import { sampleDocSearchResultsNoSortId } from '../__mocks__/es_results'; -import { sampleThresholdSignalHistory } from '../__mocks__/threshold_signal_history.mock'; import { calculateThresholdSignalUuid } from '../utils'; import { transformThresholdResultsToEcs } from './bulk_create_threshold_signals'; @@ -60,12 +58,8 @@ describe('transformThresholdNormalizedResultsToEcs', () => { 'test', startedAt, from, - undefined, - loggingSystemMock.createLogger(), threshold, - '1234', - undefined, - sampleThresholdSignalHistory() + '1234' ); const _id = calculateThresholdSignalUuid( '1234', @@ -158,12 +152,8 @@ describe('transformThresholdNormalizedResultsToEcs', () => { 'test', startedAt, from, - undefined, - loggingSystemMock.createLogger(), threshold, - '1234', - undefined, - sampleThresholdSignalHistory() + '1234' ); expect(transformedResults).toEqual({ took: 10, @@ -226,12 +216,8 @@ describe('transformThresholdNormalizedResultsToEcs', () => { 'test', startedAt, from, - undefined, - loggingSystemMock.createLogger(), threshold, - '1234', - undefined, - sampleThresholdSignalHistory() + '1234' ); const _id = calculateThresholdSignalUuid('1234', startedAt, [], ''); expect(transformedResults).toEqual({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts index f098f33b2ffc7..2148d4feacdae 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts @@ -9,10 +9,7 @@ import { TIMESTAMP } from '@kbn/rule-data-utils'; import { get } from 'lodash/fp'; import set from 'set-value'; -import { - ThresholdNormalized, - TimestampOverrideOrUndefined, -} from '../../../../../common/detection_engine/schemas/common/schemas'; +import { ThresholdNormalized } from '../../../../../common/detection_engine/schemas/common/schemas'; import { Logger } from '../../../../../../../../src/core/server'; import { AlertInstanceContext, @@ -21,7 +18,7 @@ import { } from '../../../../../../alerting/server'; import { BaseHit } from '../../../../../common/detection_engine/types'; import { TermAggregationBucket } from '../../../types'; -import { GenericBulkCreateResponse } from '../bulk_create_factory'; +import { GenericBulkCreateResponse } from '../../rule_types/factories/bulk_create_factory'; import { calculateThresholdSignalUuid, getThresholdAggregationParts } from '../utils'; import { buildReasonMessageForThresholdAlert } from '../reason_formatters'; import type { @@ -54,12 +51,8 @@ const getTransformedHits = ( inputIndex: string, startedAt: Date, from: Date, - logger: Logger, threshold: ThresholdNormalized, - ruleId: string, - filter: unknown, - timestampOverride: TimestampOverrideOrUndefined, - signalHistory: ThresholdSignalHistory + ruleId: string ) => { if (results.aggregations == null) { return []; @@ -184,24 +177,16 @@ export const transformThresholdResultsToEcs = ( inputIndex: string, startedAt: Date, from: Date, - filter: unknown, - logger: Logger, threshold: ThresholdNormalized, - ruleId: string, - timestampOverride: TimestampOverrideOrUndefined, - signalHistory: ThresholdSignalHistory + ruleId: string ): SignalSearchResponse => { const transformedHits = getTransformedHits( results, inputIndex, startedAt, from, - logger, threshold, - ruleId, - filter, - timestampOverride, - signalHistory + ruleId ); const thresholdResults = { ...results, @@ -228,12 +213,8 @@ export const bulkCreateThresholdSignals = async ( params.inputIndexPattern.join(','), params.startedAt, params.from, - params.filter, - params.logger, ruleParams.threshold, - ruleParams.ruleId, - ruleParams.timestampOverride, - params.signalHistory + ruleParams.ruleId ); return params.bulkCreate( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts index a5803dc354040..44154a8727f38 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts @@ -6,7 +6,6 @@ */ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { BoolQuery } from '@kbn/es-query'; import moment from 'moment'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { Status } from '../../../../common/detection_engine/schemas/common/schemas'; @@ -31,7 +30,7 @@ import { Logger } from '../../../../../../../src/core/server'; import { BuildRuleMessage } from './rule_messages'; import { ITelemetryEventsSender } from '../../telemetry/sender'; import { CompleteRule, RuleParams } from '../schemas/rule_schemas'; -import { GenericBulkCreateResponse } from './bulk_create_factory'; +import { GenericBulkCreateResponse } from '../rule_types/factories'; import { EcsFieldMap } from '../../../../../rule_registry/common/assets/field_maps/ecs_field_map'; import { TypeOfFieldMap } from '../../../../../rule_registry/common/field_map'; import { BuildReasonMessage } from './reason_formatters'; @@ -275,13 +274,6 @@ export interface AlertAttributes { export type BulkResponseErrorAggregation = Record; -/** - * TODO: Remove this if/when the return filter has its own type exposed - */ -export interface QueryFilter { - bool: BoolQuery; -} - export type SignalsEnrichment = (signals: SignalSearchResponse) => Promise; export type BulkCreate = >( @@ -314,9 +306,8 @@ export interface SearchAfterAndBulkCreateParams { eventsTelemetry: ITelemetryEventsSender | undefined; id: string; inputIndexPattern: string[]; - signalsIndex: string; pageSize: number; - filter: unknown; + filter: estypes.QueryDslQueryContainer; buildRuleMessage: BuildRuleMessage; buildReasonMessage: BuildReasonMessage; enrichment?: SignalsEnrichment; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/wrap_hits_factory.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/wrap_hits_factory.ts deleted file mode 100644 index 22af4dcdb9f4a..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/wrap_hits_factory.ts +++ /dev/null @@ -1,37 +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 { WrapHits, WrappedSignalHit } from './types'; -import { generateId } from './utils'; -import { buildBulkBody } from './build_bulk_body'; -import { filterDuplicateSignals } from './filter_duplicate_signals'; -import type { ConfigType } from '../../../config'; -import { CompleteRule, RuleParams } from '../schemas/rule_schemas'; - -export const wrapHitsFactory = - ({ - completeRule, - signalsIndex, - mergeStrategy, - ignoreFields, - }: { - completeRule: CompleteRule; - signalsIndex: string; - mergeStrategy: ConfigType['alertMergeStrategy']; - ignoreFields: ConfigType['alertIgnoreFields']; - }): WrapHits => - (events, buildReasonMessage) => { - const wrappedDocs: WrappedSignalHit[] = events.flatMap((doc) => [ - { - _index: signalsIndex, - _id: generateId(doc._index, doc._id, String(doc._version), completeRule.alertId ?? ''), - _source: buildBulkBody(completeRule, doc, mergeStrategy, ignoreFields, buildReasonMessage), - }, - ]); - - return filterDuplicateSignals(completeRule.alertId, wrappedDocs, false); - }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/wrap_sequences_factory.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/wrap_sequences_factory.ts deleted file mode 100644 index 3b93ae824849a..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/wrap_sequences_factory.ts +++ /dev/null @@ -1,39 +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 { WrappedSignalHit, WrapSequences } from './types'; -import { buildSignalGroupFromSequence } from './build_bulk_body'; -import { ConfigType } from '../../../config'; -import { CompleteRule, RuleParams } from '../schemas/rule_schemas'; - -export const wrapSequencesFactory = - ({ - completeRule, - signalsIndex, - mergeStrategy, - ignoreFields, - }: { - completeRule: CompleteRule; - signalsIndex: string; - mergeStrategy: ConfigType['alertMergeStrategy']; - ignoreFields: ConfigType['alertIgnoreFields']; - }): WrapSequences => - (sequences, buildReasonMessage) => - sequences.reduce( - (acc: WrappedSignalHit[], sequence) => [ - ...acc, - ...buildSignalGroupFromSequence( - sequence, - completeRule, - signalsIndex, - mergeStrategy, - ignoreFields, - buildReasonMessage - ), - ], - [] - ); From f4ed8e118f2200b56519f44461bc964e4c83be84 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Tue, 29 Mar 2022 17:06:09 +0100 Subject: [PATCH 35/37] [ML] Testing trained models in UI (#128359) * [ML] Testing trained models in UI * folder rename * code clean up * translations * adding comments * endpoint comments * small changes based on review * removing testing text * refactoring to remove duplicate code * changing misc entities * probably is now 3 sig figs * class refactor * another refactor * fixing enitiy highlighting * adding infer timeout * show class name for known types * refactoring highlighting * moving unknown entity type * removing default badge tooltips * fixing linting error * small import changes Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../plugins/ml/common/types/trained_models.ts | 22 +-- .../services/ml_api_service/index.ts | 2 + .../services/ml_api_service/trained_models.ts | 21 +++ .../models_management/models_list.tsx | 21 +++ .../models_management/test_models/index.ts | 9 + .../test_models/inference_error.tsx | 30 ++++ .../test_models/models/inference_base.ts | 30 ++++ .../models/inference_input_form.tsx | 131 ++++++++++++++ .../test_models/models/lang_ident/index.ts | 10 ++ .../models/lang_ident/lang_codes.ts | 124 +++++++++++++ .../models/lang_ident/lang_ident_inference.ts | 68 +++++++ .../models/lang_ident/lang_ident_output.tsx | 86 +++++++++ .../test_models/models/ner/index.ts | 10 ++ .../test_models/models/ner/ner_inference.ts | 59 +++++++ .../test_models/models/ner/ner_output.tsx | 167 ++++++++++++++++++ .../test_models/output_loading.tsx | 17 ++ .../test_models/selected_model.tsx | 51 ++++++ .../test_models/test_flyout.tsx | 46 +++++ .../models_management/test_models/utils.ts | 18 ++ .../ml/server/lib/ml_client/ml_client.ts | 4 + x-pack/plugins/ml/server/routes/apidoc.json | 2 + .../server/routes/schemas/inference_schema.ts | 16 ++ .../ml/server/routes/trained_models.ts | 77 ++++++++ 23 files changed, 1007 insertions(+), 14 deletions(-) create mode 100644 x-pack/plugins/ml/public/application/trained_models/models_management/test_models/index.ts create mode 100644 x-pack/plugins/ml/public/application/trained_models/models_management/test_models/inference_error.tsx create mode 100644 x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/inference_base.ts create mode 100644 x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/inference_input_form.tsx create mode 100644 x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/index.ts create mode 100644 x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/lang_codes.ts create mode 100644 x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/lang_ident_inference.ts create mode 100644 x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/lang_ident_output.tsx create mode 100644 x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/index.ts create mode 100644 x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/ner_inference.ts create mode 100644 x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/ner_output.tsx create mode 100644 x-pack/plugins/ml/public/application/trained_models/models_management/test_models/output_loading.tsx create mode 100644 x-pack/plugins/ml/public/application/trained_models/models_management/test_models/selected_model.tsx create mode 100644 x-pack/plugins/ml/public/application/trained_models/models_management/test_models/test_flyout.tsx create mode 100644 x-pack/plugins/ml/public/application/trained_models/models_management/test_models/utils.ts diff --git a/x-pack/plugins/ml/common/types/trained_models.ts b/x-pack/plugins/ml/common/types/trained_models.ts index ad59b7a917c49..182cf277d93cc 100644 --- a/x-pack/plugins/ml/common/types/trained_models.ts +++ b/x-pack/plugins/ml/common/types/trained_models.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { DataFrameAnalyticsConfig } from './data_frame_analytics'; import type { FeatureImportanceBaseline, TotalFeatureImportance } from './feature_importance'; import type { XOR } from './common'; @@ -87,14 +87,12 @@ export type PutTrainedModelConfig = { } >; // compressed_definition and definition are mutually exclusive -export interface TrainedModelConfigResponse { - description?: string; - created_by: string; - create_time: string; - default_field_map: Record; - estimated_heap_memory_usage_bytes: number; - estimated_operations: number; - license_level: string; +export type TrainedModelConfigResponse = estypes.MlTrainedModelConfig & { + /** + * Associated pipelines. Extends response from the ES endpoint. + */ + pipelines?: Record | null; + metadata?: { analytics_config: DataFrameAnalyticsConfig; input: unknown; @@ -107,11 +105,7 @@ export interface TrainedModelConfigResponse { tags: string[]; version: string; inference_config?: Record; - /** - * Associated pipelines. Extends response from the ES endpoint. - */ - pipelines?: Record | null; -} +}; export interface PipelineDefinition { processors?: Array>; diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts index 40187f70f1680..87f1a8eec2478 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts @@ -17,6 +17,7 @@ import { resultsApiProvider } from './results'; import { jobsApiProvider } from './jobs'; import { fileDatavisualizer } from './datavisualizer'; import { savedObjectsApiProvider } from './saved_objects'; +import { trainedModelsApiProvider } from './trained_models'; import type { MlServerDefaults, MlServerLimits, @@ -719,5 +720,6 @@ export function mlApiServicesProvider(httpService: HttpService) { jobs: jobsApiProvider(httpService), fileDatavisualizer, savedObjects: savedObjectsApiProvider(httpService), + trainedModels: trainedModelsApiProvider(httpService), }; } diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts index 97027b86a88e1..738f5e1ace74a 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts @@ -5,6 +5,8 @@ * 2.0. */ +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + import { useMemo } from 'react'; import { HttpFetchQuery } from 'kibana/public'; import { HttpService } from '../http_service'; @@ -138,6 +140,25 @@ export function trainedModelsApiProvider(httpService: HttpService) { query: { force }, }); }, + + inferTrainedModel(modelId: string, payload: any, timeout?: string) { + const body = JSON.stringify(payload); + return httpService.http({ + path: `${apiBasePath}/trained_models/infer/${modelId}`, + method: 'POST', + body, + ...(timeout ? { query: { timeout } as HttpFetchQuery } : {}), + }); + }, + + ingestPipelineSimulate(payload: estypes.IngestSimulateRequest['body']) { + const body = JSON.stringify(payload); + return httpService.http({ + path: `${apiBasePath}/trained_models/ingest_pipeline_simulate`, + method: 'POST', + body, + }); + }, }; } diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/models_list.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/models_list.tsx index bd3e3638e8310..1604e265b1617 100644 --- a/x-pack/plugins/ml/public/application/trained_models/models_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/models_list.tsx @@ -53,6 +53,7 @@ import { DEPLOYMENT_STATE, TRAINED_MODEL_TYPE } from '../../../../common/constan import { getUserConfirmationProvider } from './force_stop_dialog'; import { MLSavedObjectsSpacesList } from '../../components/ml_saved_objects_spaces_list'; import { SavedObjectsWarning } from '../../components/saved_objects_warning'; +import { TestTrainedModelFlyout, isTestable } from './test_models'; type Stats = Omit; @@ -134,6 +135,7 @@ export const ModelsList: FC = ({ const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState>( {} ); + const [showTestFlyout, setShowTestFlyout] = useState(null); const getUserConfirmation = useMemo(() => getUserConfirmationProvider(overlays, theme), []); const navigateToPath = useNavigateToPath(); @@ -470,6 +472,19 @@ export const ModelsList: FC = ({ return !isPopulatedObject(item.pipelines); }, }, + { + name: i18n.translate('xpack.ml.inference.modelsList.testModelActionLabel', { + defaultMessage: 'Test model', + }), + description: i18n.translate('xpack.ml.inference.modelsList.testModelActionLabel', { + defaultMessage: 'Test model', + }), + icon: 'inputOutput', + type: 'icon', + isPrimary: true, + available: isTestable, + onClick: setShowTestFlyout, + }, ] as Array>) ); } @@ -769,6 +784,12 @@ export const ModelsList: FC = ({ modelIds={modelIdsToDelete} /> )} + {showTestFlyout === null ? null : ( + + )} ); }; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/index.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/index.ts new file mode 100644 index 0000000000000..da7c12c1c0c58 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/index.ts @@ -0,0 +1,9 @@ +/* + * 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 { TestTrainedModelFlyout } from './test_flyout'; +export { isTestable } from './utils'; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/inference_error.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/inference_error.tsx new file mode 100644 index 0000000000000..dc7ae508ab270 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/inference_error.tsx @@ -0,0 +1,30 @@ +/* + * 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 React, { FC } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiCallOut } from '@elastic/eui'; + +interface Props { + errorText: string | null; +} + +export const ErrorMessage: FC = ({ errorText }) => { + return errorText === null ? null : ( + <> + +

{errorText}

+
+ + ); +}; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/inference_base.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/inference_base.ts new file mode 100644 index 0000000000000..777ca2d314c4d --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/inference_base.ts @@ -0,0 +1,30 @@ +/* + * 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 * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import { trainedModelsApiProvider } from '../../../../services/ml_api_service/trained_models'; + +const DEFAULT_INPUT_FIELD = 'text_field'; + +export type FormattedNerResp = Array<{ + value: string; + entity: estypes.MlTrainedModelEntities | null; +}>; + +export abstract class InferenceBase { + protected readonly inputField: string; + + constructor( + protected trainedModelsApi: ReturnType, + protected model: estypes.MlTrainedModelConfig + ) { + this.inputField = model.input?.field_names[0] ?? DEFAULT_INPUT_FIELD; + } + + protected abstract infer(inputText: string): Promise; +} diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/inference_input_form.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/inference_input_form.tsx new file mode 100644 index 0000000000000..6503486d98211 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/inference_input_form.tsx @@ -0,0 +1,131 @@ +/* + * 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 React, { FC, useState } from 'react'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiSpacer, EuiTextArea, EuiButton, EuiTabs, EuiTab } from '@elastic/eui'; + +import { LangIdentInference } from './lang_ident/lang_ident_inference'; +import { NerInference } from './ner/ner_inference'; +import type { FormattedLangIdentResp } from './lang_ident/lang_ident_inference'; +import type { FormattedNerResp } from './ner/ner_inference'; + +import { MLJobEditor } from '../../../../jobs/jobs_list/components/ml_job_editor'; +import { extractErrorMessage } from '../../../../../../common/util/errors'; +import { ErrorMessage } from '../inference_error'; +import { OutputLoadingContent } from '../output_loading'; + +interface Props { + inferrer: LangIdentInference | NerInference; + getOutputComponent(output: any): JSX.Element; +} + +enum TAB { + TEXT, + RAW, +} + +export const InferenceInputForm: FC = ({ inferrer, getOutputComponent }) => { + const [inputText, setInputText] = useState(''); + const [isRunning, setIsRunning] = useState(false); + const [output, setOutput] = useState(null); + const [rawOutput, setRawOutput] = useState(null); + const [selectedTab, setSelectedTab] = useState(TAB.TEXT); + const [showOutput, setShowOutput] = useState(false); + const [errorText, setErrorText] = useState(null); + + async function run() { + setShowOutput(true); + setOutput(null); + setRawOutput(null); + setIsRunning(true); + setErrorText(null); + try { + const { response, rawResponse } = await inferrer.infer(inputText); + setOutput(response); + setRawOutput(JSON.stringify(rawResponse, null, 2)); + } catch (e) { + setIsRunning(false); + setOutput(null); + setErrorText(extractErrorMessage(e)); + setRawOutput(JSON.stringify(e.body ?? e, null, 2)); + } + setIsRunning(false); + } + + return ( + <> + { + setInputText(e.target.value); + }} + /> + +
+ + + +
+ {showOutput === true ? ( + <> + + + + + + + + + + + + + {selectedTab === TAB.TEXT ? ( + <> + {errorText !== null ? ( + + ) : output === null ? ( + + ) : ( + <>{getOutputComponent(output)} + )} + + ) : ( + + )} + + ) : null} + + ); +}; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/index.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/index.ts new file mode 100644 index 0000000000000..b3439d90e8828 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/index.ts @@ -0,0 +1,10 @@ +/* + * 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 type { FormattedLangIdentResp } from './lang_ident_inference'; +export { LangIdentInference } from './lang_ident_inference'; +export { LangIdentOutput } from './lang_ident_output'; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/lang_codes.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/lang_codes.ts new file mode 100644 index 0000000000000..eff2fdcdd94e7 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/lang_codes.ts @@ -0,0 +1,124 @@ +/* + * 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. + */ + +const langCodes: Record = { + af: 'Afrikaans', + hr: 'Croatian', + pa: 'Punjabi', + am: 'Amharic', + ht: 'Haitian', + pl: 'Polish', + ar: 'Arabic', + hu: 'Hungarian', + ps: 'Pashto', + az: 'Azerbaijani', + hy: 'Armenian', + pt: 'Portuguese', + be: 'Belarusian', + id: 'Indonesian', + ro: 'Romanian', + bg: 'Bulgarian', + ig: 'Igbo', + ru: 'Russian', + 'bg-Latn': 'Bulgarian', + is: 'Icelandic', + 'ru-Latn': 'Russian', + bn: 'Bengali', + it: 'Italian', + sd: 'Sindhi', + bs: 'Bosnian', + iw: 'Hebrew', + si: 'Sinhala', + ca: 'Catalan', + ja: 'Japanese', + sk: 'Slovak', + ceb: 'Cebuano', + 'ja-Latn': 'Japanese', + sl: 'Slovenian', + co: 'Corsican', + jv: 'Javanese', + sm: 'Samoan', + cs: 'Czech', + ka: 'Georgian', + sn: 'Shona', + cy: 'Welsh', + kk: 'Kazakh', + so: 'Somali', + da: 'Danish', + km: 'Central Khmer', + sq: 'Albanian', + de: 'German', + kn: 'Kannada', + sr: 'Serbian', + el: 'Greek,modern', + ko: 'Korean', + st: 'Southern Sotho', + 'el-Latn': 'Greek,modern', + ku: 'Kurdish', + su: 'Sundanese', + en: 'English', + ky: 'Kirghiz', + sv: 'Swedish', + eo: 'Esperanto', + la: 'Latin', + sw: 'Swahili', + es: 'Spanish,Castilian', + lb: 'Luxembourgish', + ta: 'Tamil', + et: 'Estonian', + lo: 'Lao', + te: 'Telugu', + eu: 'Basque', + lt: 'Lithuanian', + tg: 'Tajik', + fa: 'Persian', + lv: 'Latvian', + th: 'Thai', + fi: 'Finnish', + mg: 'Malagasy', + tr: 'Turkish', + fil: 'Filipino', + mi: 'Maori', + uk: 'Ukrainian', + fr: 'French', + mk: 'Macedonian', + ur: 'Urdu', + fy: 'Western Frisian', + ml: 'Malayalam', + uz: 'Uzbek', + ga: 'Irish', + mn: 'Mongolian', + vi: 'Vietnamese', + gd: 'Gaelic', + mr: 'Marathi', + xh: 'Xhosa', + gl: 'Galician', + ms: 'Malay', + yi: 'Yiddish', + gu: 'Gujarati', + mt: 'Maltese', + yo: 'Yoruba', + ha: 'Hausa', + my: 'Burmese', + zh: 'Chinese', + haw: 'Hawaiian', + ne: 'Nepali', + 'zh-Latn': 'Chinese', + hi: 'Hindi', + nl: 'Dutch,Flemish', + zu: 'Zulu', + 'hi-Latn': 'Hindi', + no: 'Norwegian', + hmn: 'Hmong', + ny: 'Chichewa', + + zxx: 'unknown', +}; + +export function getLanguage(code: string) { + return langCodes[code] ?? 'unknown'; +} diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/lang_ident_inference.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/lang_ident_inference.ts new file mode 100644 index 0000000000000..9108a59197617 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/lang_ident_inference.ts @@ -0,0 +1,68 @@ +/* + * 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 * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import { InferenceBase } from '../inference_base'; + +export type FormattedLangIdentResp = Array<{ + className: string; + classProbability: number; + classScore: number; +}>; + +interface InferResponse { + response: FormattedLangIdentResp; + rawResponse: estypes.IngestSimulateResponse; +} + +export class LangIdentInference extends InferenceBase { + public async infer(inputText: string) { + const payload: estypes.IngestSimulateRequest['body'] = { + pipeline: { + processors: [ + { + inference: { + model_id: this.model.model_id, + inference_config: { + // @ts-expect-error classification missing from type + classification: { + num_top_classes: 3, + }, + }, + field_mappings: { + contents: this.inputField, + }, + target_field: '_ml.lang_ident', + }, + }, + ], + }, + docs: [ + { + _source: { + contents: inputText, + }, + }, + ], + }; + const resp = await this.trainedModelsApi.ingestPipelineSimulate(payload); + if (resp.docs.length) { + const topClasses = resp.docs[0].doc?._source._ml?.lang_ident?.top_classes ?? []; + + return { + response: topClasses.map((t: any) => ({ + className: t.class_name, + classProbability: t.class_probability, + classScore: t.class_score, + })), + rawResponse: resp, + }; + } + return { response: [], rawResponse: resp }; + } +} diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/lang_ident_output.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/lang_ident_output.tsx new file mode 100644 index 0000000000000..e4968bc516f83 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/lang_ident_output.tsx @@ -0,0 +1,86 @@ +/* + * 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 React, { FC } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiSpacer, EuiBasicTable, EuiTitle } from '@elastic/eui'; + +import type { FormattedLangIdentResp } from './lang_ident_inference'; +import { getLanguage } from './lang_codes'; + +const PROBABILITY_SIG_FIGS = 3; + +export const LangIdentOutput: FC<{ result: FormattedLangIdentResp }> = ({ result }) => { + if (result.length === 0) { + return null; + } + + const lang = getLanguage(result[0].className); + + const items = result.map(({ className, classProbability }, i) => { + return { + noa: `${i + 1}`, + className: getLanguage(className), + classProbability: `${Number(classProbability).toPrecision(PROBABILITY_SIG_FIGS)}`, + }; + }); + + const columns = [ + { + field: 'noa', + name: '#', + width: '5%', + truncateText: false, + isExpander: false, + }, + { + field: 'className', + name: i18n.translate( + 'xpack.ml.trainedModels.testModelsFlyout.langIdent.output.language_title', + { + defaultMessage: 'Language', + } + ), + width: '30%', + truncateText: false, + isExpander: false, + }, + { + field: 'classProbability', + name: i18n.translate( + 'xpack.ml.trainedModels.testModelsFlyout.langIdent.output.probability_title', + { + defaultMessage: 'Probability', + } + ), + truncateText: false, + isExpander: false, + }, + ]; + + const title = + lang !== 'unknown' + ? i18n.translate('xpack.ml.trainedModels.testModelsFlyout.langIdent.output.title', { + defaultMessage: 'This looks like {lang}', + values: { lang }, + }) + : i18n.translate('xpack.ml.trainedModels.testModelsFlyout.langIdent.output.titleUnknown', { + defaultMessage: 'Language code unknown: {code}', + values: { code: result[0].className }, + }); + + return ( + <> + +

{title}

+
+ + + + + ); +}; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/index.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/index.ts new file mode 100644 index 0000000000000..38ddad8bdeb80 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/index.ts @@ -0,0 +1,10 @@ +/* + * 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 type { FormattedNerResp } from './ner_inference'; +export { NerInference } from './ner_inference'; +export { NerOutput } from './ner_output'; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/ner_inference.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/ner_inference.ts new file mode 100644 index 0000000000000..e4dcfcc2c6333 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/ner_inference.ts @@ -0,0 +1,59 @@ +/* + * 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 * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import { InferenceBase } from '../inference_base'; + +export type FormattedNerResp = Array<{ + value: string; + entity: estypes.MlTrainedModelEntities | null; +}>; + +interface InferResponse { + response: FormattedNerResp; + rawResponse: estypes.MlInferTrainedModelDeploymentResponse; +} + +export class NerInference extends InferenceBase { + public async infer(inputText: string) { + const payload = { docs: { [this.inputField]: inputText } }; + const resp = await this.trainedModelsApi.inferTrainedModel(this.model.model_id, payload, '30s'); + + return { response: parseResponse(resp), rawResponse: resp }; + } +} + +function parseResponse(resp: estypes.MlInferTrainedModelDeploymentResponse): FormattedNerResp { + const { predicted_value: predictedValue, entities } = resp; + const splitWordsAndEntitiesRegex = /(\[.*?\]\(.*?&.*?\))/; + const matchEntityRegex = /(\[.*?\])\((.*?)&(.*?)\)/; + if (predictedValue === undefined || entities === undefined) { + return []; + } + + const sentenceChunks = (predictedValue as unknown as string).split(splitWordsAndEntitiesRegex); + let count = 0; + return sentenceChunks.map((chunk) => { + const matchedEntity = chunk.match(matchEntityRegex); + if (matchedEntity) { + const entityValue = matchedEntity[3]; + const entity = entities[count]; + if (entityValue !== entity.entity && entityValue.replaceAll('+', ' ') !== entity.entity) { + // entityValue may not equal entity.entity if the entity is comprised of + // two words as they are joined with a plus symbol + // Replace any plus symbols and check again. If they still don't match, log an error + + // eslint-disable-next-line no-console + console.error('mismatch entity', entity); + } + count++; + return { value: entity.entity, entity }; + } + return { value: chunk, entity: null }; + }); +} diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/ner_output.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/ner_output.tsx new file mode 100644 index 0000000000000..e9db3fa8efd36 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/ner_output.tsx @@ -0,0 +1,167 @@ +/* + * 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 * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import React, { FC, ReactNode } from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { + EuiHorizontalRule, + EuiBadge, + EuiToolTip, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, +} from '@elastic/eui'; + +import { + useCurrentEuiTheme, + EuiThemeType, +} from '../../../../../components/color_range_legend/use_color_range'; +import type { FormattedNerResp } from './ner_inference'; + +const ICON_PADDING = '2px'; +const PROBABILITY_SIG_FIGS = 3; + +const ENTITY_TYPES = { + PER: { + label: 'Person', + icon: 'user', + color: 'euiColorVis5_behindText', + borderColor: 'euiColorVis5', + }, + LOC: { + label: 'Location', + icon: 'visMapCoordinate', + color: 'euiColorVis1_behindText', + borderColor: 'euiColorVis1', + }, + ORG: { + label: 'Organization', + icon: 'home', + color: 'euiColorVis0_behindText', + borderColor: 'euiColorVis0', + }, + MISC: { + label: 'Miscellaneous', + icon: 'questionInCircle', + color: 'euiColorVis7_behindText', + borderColor: 'euiColorVis7', + }, +}; + +const UNKNOWN_ENTITY_TYPE = { + label: '', + icon: 'questionInCircle', + color: 'euiColorVis5_behindText', + borderColor: 'euiColorVis5', +}; + +export const NerOutput: FC<{ result: FormattedNerResp }> = ({ result }) => { + const { euiTheme } = useCurrentEuiTheme(); + const lineSplit: JSX.Element[] = []; + result.forEach(({ value, entity }) => { + if (entity === null) { + const lines = value + .split(/(\n)/) + .map((line) => (line === '\n' ?
: {line})); + + lineSplit.push(...lines); + } else { + lineSplit.push( + +
+ + {value} +
+ +
+
+ + : {getClassLabel(entity.class_name)} +
+
+ + : {Number(entity.class_probability).toPrecision(PROBABILITY_SIG_FIGS)} +
+
+
+ } + > + {value} + + ); + } + }); + return
{lineSplit}
; +}; + +const EntityBadge = ({ + entity, + children, +}: { + entity: estypes.MlTrainedModelEntities; + children: ReactNode; +}) => { + const { euiTheme } = useCurrentEuiTheme(); + return ( + + + + + + {children} + + + ); +}; + +function getClassIcon(className: string) { + const entity = ENTITY_TYPES[className as keyof typeof ENTITY_TYPES]; + return entity?.icon ?? UNKNOWN_ENTITY_TYPE.icon; +} + +function getClassLabel(className: string) { + const entity = ENTITY_TYPES[className as keyof typeof ENTITY_TYPES]; + return entity?.label ?? className; +} + +function getClassColor(euiTheme: EuiThemeType, className: string, border: boolean = false) { + const entity = ENTITY_TYPES[className as keyof typeof ENTITY_TYPES]; + let color = entity?.color ?? UNKNOWN_ENTITY_TYPE.color; + if (border) { + color = entity?.borderColor ?? UNKNOWN_ENTITY_TYPE.borderColor; + } + return euiTheme[color as keyof typeof euiTheme]; +} diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/output_loading.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/output_loading.tsx new file mode 100644 index 0000000000000..4cceed23edd25 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/output_loading.tsx @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC } from 'react'; +import { EuiLoadingContent } from '@elastic/eui'; +import { LineRange } from '@elastic/eui/src/components/loading/loading_content'; + +export const OutputLoadingContent: FC<{ text: string }> = ({ text }) => { + const actualLines = text.split(/\r\n|\r|\n/).length + 1; + const lines = actualLines > 4 && actualLines <= 10 ? actualLines : 4; + + return ; +}; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/selected_model.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/selected_model.tsx new file mode 100644 index 0000000000000..cab0826d5584a --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/selected_model.tsx @@ -0,0 +1,51 @@ +/* + * 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 * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import React, { FC } from 'react'; + +import { NerOutput, NerInference } from './models/ner'; +import type { FormattedNerResp } from './models/ner'; +import { LangIdentOutput, LangIdentInference } from './models/lang_ident'; +import type { FormattedLangIdentResp } from './models/lang_ident'; + +import { TRAINED_MODEL_TYPE } from '../../../../../common/constants/trained_models'; +import { useMlApiContext } from '../../../contexts/kibana'; +import { InferenceInputForm } from './models/inference_input_form'; + +interface Props { + model: estypes.MlTrainedModelConfig | null; +} + +export const SelectedModel: FC = ({ model }) => { + const { trainedModels } = useMlApiContext(); + + if (model === null) { + return null; + } + + if (model.model_type === TRAINED_MODEL_TYPE.PYTORCH) { + const inferrer = new NerInference(trainedModels, model); + return ( + } + /> + ); + } + if (model.model_type === TRAINED_MODEL_TYPE.LANG_IDENT) { + const inferrer = new LangIdentInference(trainedModels, model); + return ( + } + /> + ); + } + + return null; +}; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/test_flyout.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/test_flyout.tsx new file mode 100644 index 0000000000000..343cd32addce7 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/test_flyout.tsx @@ -0,0 +1,46 @@ +/* + * 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 * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import React, { FC } from 'react'; + +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiFlyout, EuiFlyoutHeader, EuiTitle, EuiFlyoutBody, EuiSpacer } from '@elastic/eui'; + +import { SelectedModel } from './selected_model'; + +interface Props { + model: estypes.MlTrainedModelConfig; + onClose: () => void; +} +export const TestTrainedModelFlyout: FC = ({ model, onClose }) => { + return ( + <> + + + +

+ +

+
+
+ + +

{model.model_id}

+
+ + + + +
+
+ + ); +}; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/utils.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/utils.ts new file mode 100644 index 0000000000000..ccddd960349d2 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/utils.ts @@ -0,0 +1,18 @@ +/* + * 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 * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { TRAINED_MODEL_TYPE } from '../../../../../common/constants/trained_models'; + +const TESTABLE_MODEL_TYPES: estypes.MlTrainedModelType[] = [ + TRAINED_MODEL_TYPE.PYTORCH, + TRAINED_MODEL_TYPE.LANG_IDENT, +]; + +export function isTestable(model: estypes.MlTrainedModelConfig) { + return model.model_type && TESTABLE_MODEL_TYPES.includes(model.model_type); +} diff --git a/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts b/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts index 342a3913a6cba..122162777d9a5 100644 --- a/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts +++ b/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts @@ -494,6 +494,10 @@ export function getMlClient( await modelIdsCheck(p); return mlClient.stopTrainedModelDeployment(...p); }, + async inferTrainedModelDeployment(...p: Parameters) { + await modelIdsCheck(p); + return mlClient.inferTrainedModelDeployment(...p); + }, async info(...p: Parameters) { return mlClient.info(...p); }, diff --git a/x-pack/plugins/ml/server/routes/apidoc.json b/x-pack/plugins/ml/server/routes/apidoc.json index 59ed08664da3b..ac09aee7fcbb9 100644 --- a/x-pack/plugins/ml/server/routes/apidoc.json +++ b/x-pack/plugins/ml/server/routes/apidoc.json @@ -171,6 +171,8 @@ "StopTrainedModelDeployment", "PutTrainedModel", "DeleteTrainedModel", + "InferTrainedModelDeployment", + "IngestPipelineSimulate", "Alerting", "PreviewAlert" diff --git a/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts b/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts index 941edb31c79fa..1b9a865dcfca9 100644 --- a/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts @@ -30,3 +30,19 @@ export const getInferenceQuerySchema = schema.object({ export const putTrainedModelQuerySchema = schema.object({ defer_definition_decompression: schema.maybe(schema.boolean()), }); + +export const pipelineSchema = schema.object({ + pipeline: schema.object({ + description: schema.maybe(schema.string()), + processors: schema.arrayOf(schema.recordOf(schema.string(), schema.any())), + version: schema.maybe(schema.number()), + on_failure: schema.maybe(schema.arrayOf(schema.recordOf(schema.string(), schema.any()))), + }), + docs: schema.arrayOf(schema.recordOf(schema.string(), schema.any())), + verbose: schema.maybe(schema.boolean()), +}); + +export const inferTrainedModelQuery = schema.object({ timeout: schema.maybe(schema.string()) }); +export const inferTrainedModelBody = schema.object({ + docs: schema.any(), +}); diff --git a/x-pack/plugins/ml/server/routes/trained_models.ts b/x-pack/plugins/ml/server/routes/trained_models.ts index 887ad47f1ceb2..27a062b45767c 100644 --- a/x-pack/plugins/ml/server/routes/trained_models.ts +++ b/x-pack/plugins/ml/server/routes/trained_models.ts @@ -5,6 +5,7 @@ * 2.0. */ +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { schema } from '@kbn/config-schema'; import { RouteInitialization } from '../types'; import { wrapError } from '../client/error_wrapper'; @@ -13,6 +14,9 @@ import { modelIdSchema, optionalModelIdSchema, putTrainedModelQuerySchema, + pipelineSchema, + inferTrainedModelQuery, + inferTrainedModelBody, } from './schemas/inference_schema'; import { modelsProvider } from '../models/data_frame_analytics'; import { TrainedModelConfigResponse } from '../../common/types/trained_models'; @@ -352,4 +356,77 @@ export function trainedModelsRoutes({ router, routeGuard }: RouteInitialization) } }) ); + + /** + * @apiGroup TrainedModels + * + * @api {post} /api/ml/trained_models/infer/:modelId Evaluates a trained model + * @apiName InferTrainedModelDeployment + * @apiDescription Evaluates a trained model. + */ + router.post( + { + path: '/api/ml/trained_models/infer/{modelId}', + validate: { + params: modelIdSchema, + query: inferTrainedModelQuery, + body: inferTrainedModelBody, + }, + options: { + tags: ['access:ml:canStartStopTrainedModels'], + }, + }, + routeGuard.fullLicenseAPIGuard(async ({ mlClient, request, response }) => { + try { + const { modelId } = request.params; + const body = await mlClient.inferTrainedModelDeployment({ + model_id: modelId, + docs: request.body.docs, + ...(request.query.timeout ? { timeout: request.query.timeout } : {}), + }); + return response.ok({ + body, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + + /** + * @apiGroup TrainedModels + * + * @api {post} /api/ml/trained_models/ingest_pipeline_simulate Ingest pipeline simulate + * @apiName IngestPipelineSimulate + * @apiDescription Simulates an ingest pipeline call using supplied documents + */ + router.post( + { + path: '/api/ml/trained_models/ingest_pipeline_simulate', + validate: { + body: pipelineSchema, + }, + options: { + tags: ['access:ml:canStartStopTrainedModels'], + }, + }, + routeGuard.fullLicenseAPIGuard(async ({ client, request, response }) => { + try { + const { pipeline, docs, verbose } = request.body; + + const body = await client.asCurrentUser.ingest.simulate({ + verbose, + body: { + pipeline, + docs: docs as estypes.IngestSimulateDocument[], + }, + }); + return response.ok({ + body, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); } From fc9a0ff1d80c26021faa6517f7c8bb81029d1e0e Mon Sep 17 00:00:00 2001 From: Marta Bondyra Date: Tue, 29 Mar 2022 18:25:12 +0200 Subject: [PATCH 36/37] [Lens] fix displaying position options for ref lines --- .../xy_config_panel/reference_line_panel.tsx | 9 ++++++--- .../shared/marker_decoration_settings.tsx | 20 +++++++++++++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx index ffca2c0531b7c..8d7bf65ef66d7 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx @@ -22,7 +22,10 @@ import { updateLayer } from '.'; import { useDebouncedValue } from '../../shared_components'; import { idPrefix } from './dimension_editor'; import { isHorizontalChart } from '../state_helpers'; -import { MarkerDecorationSettings } from './shared/marker_decoration_settings'; +import { + MarkerDecorationPosition, + MarkerDecorationSettings, +} from './shared/marker_decoration_settings'; import { LineStyleSettings } from './shared/line_style_settings'; export const ReferenceLinePanel = ( @@ -72,8 +75,8 @@ export const ReferenceLinePanel = ( return ( <> - {' '} - + void; - isHorizontal: boolean; customIconSet?: IconSet; }) => { return ( @@ -146,8 +144,22 @@ export const MarkerDecorationSettings = ({ }} /> - {currentConfig?.iconPosition && - (hasIcon(currentConfig?.icon) || currentConfig?.textVisibility) ? ( + + ); +}; + +export const MarkerDecorationPosition = ({ + currentConfig, + setConfig, + isHorizontal, +}: { + currentConfig?: MarkerDecorationConfig; + setConfig: (config: MarkerDecorationConfig) => void; + isHorizontal: boolean; +}) => { + return ( + <> + {hasIcon(currentConfig?.icon) || currentConfig?.textVisibility ? ( Date: Tue, 29 Mar 2022 19:44:54 +0200 Subject: [PATCH 37/37] fix types --- .../public/xy_visualization/annotations/config_panel/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/lens/public/xy_visualization/annotations/config_panel/index.tsx b/x-pack/plugins/lens/public/xy_visualization/annotations/config_panel/index.tsx index c27165accb81d..44eee0004c076 100644 --- a/x-pack/plugins/lens/public/xy_visualization/annotations/config_panel/index.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/annotations/config_panel/index.tsx @@ -102,7 +102,6 @@ export const AnnotationsPanel = ( }} />