diff --git a/public/components/Charts/ChartContainer.scss b/public/components/Charts/ChartContainer.scss index 0cfaa2fa6..2eeb2a340 100644 --- a/public/components/Charts/ChartContainer.scss +++ b/public/components/Charts/ChartContainer.scss @@ -1,6 +1,6 @@ .chart-view-container { position: relative; - width: 100%; + width: 90%; height: 100%; .chart-view-container-mask { diff --git a/public/components/DataSourceThreatAlertsCard/DataSourceThreatAlertsCard.tsx b/public/components/DataSourceThreatAlertsCard/DataSourceThreatAlertsCard.tsx index 32891aceb..5a2cd5cca 100644 --- a/public/components/DataSourceThreatAlertsCard/DataSourceThreatAlertsCard.tsx +++ b/public/components/DataSourceThreatAlertsCard/DataSourceThreatAlertsCard.tsx @@ -5,7 +5,6 @@ import React, { useCallback, useEffect, useState } from 'react'; import { - EuiBadge, EuiBasicTable, EuiBasicTableColumn, EuiFlexGroup, @@ -23,10 +22,9 @@ import { import { dataSourceFilterFn, errorNotificationToast, - getBadgeText, - getSeverityColor, getTruncatedText, renderTime, + getAlertSeverityBadge, } from '../../utils/helpers'; import { THREAT_ALERTS_NAV_ID } from '../../utils/constants'; import { @@ -137,17 +135,7 @@ export const DataSourceThreatAlertsCard: React.FC = ( name: 'Alert severity', sortable: true, align: 'left', - render: (severity: string) => { - const severityColor = getSeverityColor(severity); - return ( - - {getBadgeText(severity)} - - ); - }, + render: getAlertSeverityBadge, }, ]; diff --git a/public/components/MDS/DataSourceMenuWrapper.tsx b/public/components/MDS/DataSourceMenuWrapper.tsx index 9a5c1049e..69ddc8419 100644 --- a/public/components/MDS/DataSourceMenuWrapper.tsx +++ b/public/components/MDS/DataSourceMenuWrapper.tsx @@ -159,6 +159,7 @@ export const DataSourceMenuWrapper: React.FC = ({ ROUTES.RULES_DUPLICATE, ROUTES.LOG_TYPES_CREATE, ROUTES.CORRELATION_RULE_CREATE, + ROUTES.GETTING_STARTED, ROUTES.ROOT, ]} render={() => { diff --git a/public/components/PageHeader/PageHeader.tsx b/public/components/PageHeader/PageHeader.tsx index bf1770129..548967cdf 100644 --- a/public/components/PageHeader/PageHeader.tsx +++ b/public/components/PageHeader/PageHeader.tsx @@ -7,14 +7,13 @@ import React from 'react'; import { TopNavControlData, TopNavControlDescriptionData, - TopNavControlLinkData, } from '../../../../../src/plugins/navigation/public'; import { getApplication, getNavigationUI, getUseUpdatedUx } from '../../services/utils/constants'; export interface PageHeaderProps { appRightControls?: TopNavControlData[]; appBadgeControls?: TopNavControlData[]; - appDescriptionControls?: (TopNavControlDescriptionData | TopNavControlLinkData)[]; + appDescriptionControls?: TopNavControlDescriptionData[]; } export const PageHeader: React.FC = ({ diff --git a/public/pages/CreateDetector/components/ConfigureAlerts/components/AlertCondition/__snapshots__/AlertConditionPanel.test.tsx.snap b/public/pages/CreateDetector/components/ConfigureAlerts/components/AlertCondition/__snapshots__/AlertConditionPanel.test.tsx.snap index 6fe293be6..c28c89fc3 100644 --- a/public/pages/CreateDetector/components/ConfigureAlerts/components/AlertCondition/__snapshots__/AlertConditionPanel.test.tsx.snap +++ b/public/pages/CreateDetector/components/ConfigureAlerts/components/AlertCondition/__snapshots__/AlertConditionPanel.test.tsx.snap @@ -476,7 +476,6 @@ Object { tabindex="-1" > = name: 'Threat intel source', field: 'ioc_feed_ids', render: (ioc_feed_ids: ThreatIntelFinding['ioc_feed_ids']) => { - return {ioc_feed_ids[0]?.feed_id ?? DEFAULT_EMPTY_DATA}; + return ( + {ioc_feed_ids.map((ids) => ids.feed_name).join(', ') || DEFAULT_EMPTY_DATA} + ); }, }, { diff --git a/public/pages/Main/Main.tsx b/public/pages/Main/Main.tsx index 78fe978f8..463aa64f5 100644 --- a/public/pages/Main/Main.tsx +++ b/public/pages/Main/Main.tsx @@ -18,9 +18,14 @@ import { EuiFlexItem, } from '@elastic/eui'; import { Toast } from '@opensearch-project/oui/src/eui_components/toast/global_toast_list'; -import { AppMountParameters, CoreStart, SavedObject } from 'opensearch-dashboards/public'; +import { AppMountParameters, CoreStart } from 'opensearch-dashboards/public'; import { SaContextConsumer } from '../../services'; -import { DEFAULT_DATE_RANGE, DATE_TIME_FILTER_KEY, ROUTES, dataSourceObservable } from '../../utils/constants'; +import { + DEFAULT_DATE_RANGE, + DATE_TIME_FILTER_KEY, + ROUTES, + dataSourceObservable, +} from '../../utils/constants'; import { CoreServicesConsumer } from '../../components/core_services'; import Findings from '../Findings'; import Detectors from '../Detectors'; @@ -56,15 +61,14 @@ import { DataSourceManagementPluginSetup } from '../../../../../src/plugins/data import { DataSourceMenuWrapper } from '../../components/MDS/DataSourceMenuWrapper'; import { DataSourceOption } from 'src/plugins/data_source_management/public/components/data_source_menu/types'; import { DataSourceContext, DataSourceContextConsumer } from '../../services/DataSourceContext'; -import { dataSourceInfo } from '../../services/utils/constants'; +import { dataSourceInfo, getUseUpdatedUx } from '../../services/utils/constants'; import { ThreatIntelOverview } from '../ThreatIntel/containers/Overview/ThreatIntelOverview'; import { AddThreatIntelSource } from '../ThreatIntel/containers/AddThreatIntelSource/AddThreatIntelSource'; import { ThreatIntelScanConfigForm } from '../ThreatIntel/containers/ScanConfiguration/ThreatIntelScanConfigForm'; import { ThreatIntelSource } from '../ThreatIntel/containers/ThreatIntelSource/ThreatIntelSource'; -import * as pluginManifest from '../../../opensearch_dashboards.json'; -import { DataSourceAttributes } from '../../../../../src/plugins/data_source/common/data_sources'; -import semver from 'semver'; import queryString from 'query-string'; +import { dataSourceFilterFn } from '../../utils/helpers'; +import { GettingStartedContent } from '../Overview/components/GettingStarted/GettingStartedContent'; enum Navigation { SecurityAnalytics = 'Security Analytics', @@ -137,20 +141,21 @@ export default class Main extends Component { const defaultDateTimeFilter = cachedDateTimeFilter ? JSON.parse(cachedDateTimeFilter) : { - startTime: DEFAULT_DATE_RANGE.start, - endTime: DEFAULT_DATE_RANGE.end, - }; - let dataSourceId = ""; - let dataSourceLabel = ""; + startTime: DEFAULT_DATE_RANGE.start, + endTime: DEFAULT_DATE_RANGE.end, + }; + let dataSourceId = ''; + let dataSourceLabel = ''; if (props.multiDataSourceEnabled) { - const { dataSourceId: parsedDataSourceId, dataSourceLabel: parsedDataSourceLabel } = queryString.parse( - this.props.location.search - ) as { + const { + dataSourceId: parsedDataSourceId, + dataSourceLabel: parsedDataSourceLabel, + } = queryString.parse(this.props.location.search) as { dataSourceId: string; dataSourceLabel: string; }; dataSourceId = parsedDataSourceId; - dataSourceLabel = parsedDataSourceLabel || ""; + dataSourceLabel = parsedDataSourceLabel || ''; if (dataSourceId) { dataSourceObservable.next({ id: dataSourceId, label: dataSourceLabel }); @@ -163,10 +168,10 @@ export default class Main extends Component { dateTimeFilter: defaultDateTimeFilter, showFlyoutData: null, /** - * undefined: need data source picker to help to determine which data source to use. - * empty string: using the local cluster. - * string: using the selected data source. - */ + * undefined: need data source picker to help to determine which data source to use. + * empty string: using the local cluster. + * string: using the selected data source. + */ dataSourceLoading: dataSourceId === undefined ? props.multiDataSourceEnabled : false, selectedDataSource: { id: dataSourceId }, dataSourceMenuReadOnly: false, @@ -267,7 +272,10 @@ export default class Main extends Component { selectedDataSource: { ...sources[0] }, }); } - dataSourceObservable.next({ id: this.state.selectedDataSource.id, label: this.state.selectedDataSource.label }); + dataSourceObservable.next({ + id: this.state.selectedDataSource.id, + label: this.state.selectedDataSource.label, + }); if (dataSourceLoading) { this.setState({ dataSourceLoading: false }); } @@ -406,17 +414,6 @@ export default class Main extends Component { ]; }; - dataSourceFilterFn = (dataSource: SavedObject) => { - const dataSourceVersion = dataSource?.attributes?.dataSourceVersion || ''; - const installedPlugins = dataSource?.attributes?.installedPlugins || []; - return ( - semver.satisfies(dataSourceVersion, pluginManifest.supportedOSDataSourceVersions) && - pluginManifest.requiredOSDataSourcePlugins.every((plugin) => - installedPlugins.includes(plugin) - ) - ); - }; - render() { const { landingPage, @@ -463,7 +460,7 @@ export default class Main extends Component { dataSourceLoading={this.state.dataSourceLoading} dataSourceMenuReadOnly={dataSourceMenuReadOnly} setHeaderActionMenu={setActionMenu} - dataSourceFilterFn={this.dataSourceFilterFn} + dataSourceFilterFn={dataSourceFilterFn} /> )} {!dataSourceLoading && services && ( @@ -620,6 +617,17 @@ export default class Main extends Component { /> )} /> + {getUseUpdatedUx() && ( + ( + {}} + /> + )} + /> + )} ( diff --git a/public/pages/Overview/components/GettingStarted/GettingStartedContent.tsx b/public/pages/Overview/components/GettingStarted/GettingStartedContent.tsx new file mode 100644 index 000000000..14a913bba --- /dev/null +++ b/public/pages/Overview/components/GettingStarted/GettingStartedContent.tsx @@ -0,0 +1,210 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { useMemo } from 'react'; +import { + EuiHorizontalRule, + EuiLink, + EuiPanel, + EuiSpacer, + EuiSteps, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; +import { + BREADCRUMBS, + DETECTORS_NAV_ID, + DETECTION_RULE_NAV_ID, + FINDINGS_NAV_ID, + ROUTES, + THREAT_ALERTS_NAV_ID, + THREAT_INTEL_NAV_ID, + CORRELATIONS_RULE_NAV_ID, +} from '../../../../utils/constants'; +import { RouteComponentProps } from 'react-router-dom'; +import { GetStartedStep } from './GetStartedStep'; +import { moreLink } from '../../utils/constants'; +import { setBreadcrumbs } from '../../../../utils/helpers'; +import { getApplication, getUseUpdatedUx } from '../../../../services/utils/constants'; +import { PageHeader } from '../../../../components/PageHeader/PageHeader'; + +export interface GettingStartedPopupProps { + onStepClicked: () => void; + history: RouteComponentProps['history']; +} + +export const GettingStartedContent: React.FC = ({ + onStepClicked, + history, +}) => { + const useUpdatedUx = getUseUpdatedUx(); + if (useUpdatedUx) { + setBreadcrumbs([BREADCRUMBS.GETTING_STARTED]); + } + const onActionClick = (appId: string, route: string) => { + if (useUpdatedUx) { + const url = getApplication().getUrlForApp(appId, { path: `#${route}` }); + getApplication().navigateToUrl(url); + } else { + history.push(route); + } + }; + + const steps: EuiContainedStepProps[] = useMemo( + () => [ + { + title: 'Create security detector', + children: ( + { + onStepClicked(); + onActionClick(DETECTORS_NAV_ID, ROUTES.DETECTORS_CREATE); + }, + opts: { + fill: true, + }, + }, + ]} + /> + ), + }, + { + title: 'Set up threat intelligence analytics', + children: ( + { + onStepClicked(); + onActionClick(THREAT_INTEL_NAV_ID, ROUTES.THREAT_INTEL_OVERVIEW); + }, + }, + ]} + /> + ), + }, + { + title: 'Discover security findings', + children: ( + { + onStepClicked(); + onActionClick(FINDINGS_NAV_ID, ROUTES.FINDINGS); + }, + }, + ]} + /> + ), + }, + { + title: 'View security alerts', + children: ( + { + onStepClicked(); + onActionClick(THREAT_ALERTS_NAV_ID, ROUTES.ALERTS); + }, + }, + ]} + /> + ), + }, + { + title: 'Create custom rules for detectors', + children: ( + { + onStepClicked(); + onActionClick(DETECTION_RULE_NAV_ID, ROUTES.RULES); + }, + }, + ]} + /> + ), + }, + { + title: 'Set up correlation rules', + children: ( + { + onStepClicked(); + onActionClick(CORRELATIONS_RULE_NAV_ID, ROUTES.CORRELATION_RULES); + }, + }, + ]} + /> + ), + }, + ], + [onStepClicked] + ); + + const stepsComponent = ; + + return ( + <> + + +

Get started with Security analytics

+
+ + +

+ Generates critical security insights from your event logs.  + + Learn more + +

+
+ +
+ {useUpdatedUx ? {stepsComponent} : stepsComponent} + + ); +}; diff --git a/public/pages/Overview/components/GettingStarted/GettingStartedPopup.tsx b/public/pages/Overview/components/GettingStarted/GettingStartedPopup.tsx deleted file mode 100644 index 62a086f7b..000000000 --- a/public/pages/Overview/components/GettingStarted/GettingStartedPopup.tsx +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React, { useMemo } from 'react'; -import { EuiHorizontalRule, EuiLink, EuiSpacer, EuiSteps, EuiText, EuiTitle } from '@elastic/eui'; -import { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; -import { ROUTES } from '../../../../utils/constants'; -import { RouteComponentProps } from 'react-router-dom'; -import { GetStartedStep } from './GetStartedStep'; -import { moreLink } from '../../utils/constants'; - -export interface GettingStartedPopupProps { - dismissPopup: () => void; - history: RouteComponentProps['history']; -} - -export const GettingStartedPopup: React.FC = ({ - dismissPopup, - history, -}) => { - const steps: EuiContainedStepProps[] = useMemo( - () => [ - { - title: 'Create security detector', - children: ( - { - dismissPopup(); - history.push(ROUTES.DETECTORS_CREATE); - }, - opts: { - fill: true, - }, - }, - ]} - /> - ), - }, - { - title: 'Discover security findings', - children: ( - { - dismissPopup(); - history.push(ROUTES.FINDINGS); - }, - }, - ]} - /> - ), - }, - { - title: 'View security alerts', - children: ( - { - dismissPopup(); - history.push(ROUTES.ALERTS); - }, - }, - ]} - /> - ), - }, - { - title: 'Create custom rules for detectors', - children: ( - { - dismissPopup(); - history.push(ROUTES.RULES); - }, - }, - ]} - /> - ), - }, - ], - [dismissPopup] - ); - - return ( - <> - -

Get started with Security analytics

-
- - -

- Generates critical security insights from your event logs.  - - Learn more - -

-
- - - - ); -}; diff --git a/public/pages/Overview/components/Widgets/RecentAlertsWidget.tsx b/public/pages/Overview/components/Widgets/RecentAlertsWidget.tsx index 14bedd4f5..b719ee682 100644 --- a/public/pages/Overview/components/Widgets/RecentAlertsWidget.tsx +++ b/public/pages/Overview/components/Widgets/RecentAlertsWidget.tsx @@ -4,12 +4,11 @@ */ import { EuiBasicTableColumn, EuiSmallButton, EuiEmptyPrompt } from '@elastic/eui'; -import { DEFAULT_EMPTY_DATA, ROUTES, SortDirection } from '../../../../utils/constants'; +import { ROUTES, SortDirection } from '../../../../utils/constants'; import React, { useEffect, useState } from 'react'; import { TableWidget } from './TableWidget'; import { WidgetContainer } from './WidgetContainer'; -import { parseAlertSeverityToOption } from '../../../CreateDetector/components/ConfigureAlerts/utils/helpers'; -import { renderTime } from '../../../../utils/helpers'; +import { getAlertSeverityBadge, renderTime } from '../../../../utils/helpers'; import { OverviewAlertItem } from '../../../../../types'; const columns: EuiBasicTableColumn[] = [ @@ -31,7 +30,7 @@ const columns: EuiBasicTableColumn[] = [ name: 'Alert severity', sortable: true, align: 'left', - render: (severity: string) => parseAlertSeverityToOption(severity)?.label || DEFAULT_EMPTY_DATA, + render: (severity: string) => getAlertSeverityBadge(severity), }, ]; @@ -76,7 +75,7 @@ export const RecentAlertsWidget: React.FC = ({ ); return ( - + [] = [ { @@ -32,7 +33,7 @@ const columns: EuiBasicTableColumn[] = [ sortable: false, align: 'left', width: '20%', - render: (ruleSeverity: string) => capitalizeFirstLetter(ruleSeverity), + render: (ruleSeverity: string) => getSeverityBadge(ruleSeverity), // capitalizeFirstLetter(ruleSeverity), }, { field: 'detector', @@ -75,13 +76,13 @@ export const RecentFindingsWidget: React.FC = ({ ); }, [items]); - const actions = React.useMemo( - () => [View all findings], - [] - ); + const actions = React.useMemo(() => { + const baseUrl = getUseUpdatedUx() ? getApplication().getUrlForApp(FINDINGS_NAV_ID) : ''; + return [View all]; + }, []); return ( - + [] = [ + { + name: 'Time', + field: 'timestamp', + render: (timestamp: number) => renderTime(timestamp), + }, + { + name: 'Indicator of compromise', + field: 'ioc_value', + }, + { + name: 'Indicator type', + field: 'ioc_type', + render: (iocType: ThreatIntelIocType) => IocLabel[iocType], + }, + { + name: 'Threat intel source', + field: 'ioc_feed_ids', + render: (ioc_feed_ids: ThreatIntelFinding['ioc_feed_ids']) => { + return ( + {ioc_feed_ids.map((ids) => ids.feed_name).join(', ') || DEFAULT_EMPTY_DATA} + ); + }, + }, +]; + +export interface RecentThreatIntelFindingsWidgetProps { + items: ThreatIntelFinding[]; + loading?: boolean; +} + +export const RecentThreatIntelFindingsWidget: React.FC = ({ + items, + loading = false, +}) => { + const [findingItems, setFindingItems] = useState([]); + const [widgetEmptyMessage, setWidgetEmptyMessage] = useState( + undefined + ); + + useEffect(() => { + items.sort((a, b) => { + return b.timestamp - a.timestamp; + }); + setFindingItems(items.slice(0, 20)); + setWidgetEmptyMessage( + items.length > 0 ? undefined : ( + + No recent findings.Adjust the time range to + see more results. +

+ } + /> + ) + ); + }, [items]); + + const threatIntelFindingsUrl = `${getApplication().getUrlForApp(FINDINGS_NAV_ID, { + path: `#${ROUTES.FINDINGS}`, + })}?detectionType=${FindingTabId.ThreatIntel}`; + const actions = React.useMemo( + () => [ + getApplication().navigateToUrl(threatIntelFindingsUrl)}> + View all + , + ], + [] + ); + + return ( + + + + ); +}; diff --git a/public/pages/Overview/components/Widgets/Summary.tsx b/public/pages/Overview/components/Widgets/Summary.tsx index 58f09264e..2e2a50f60 100644 --- a/public/pages/Overview/components/Widgets/Summary.tsx +++ b/public/pages/Overview/components/Widgets/Summary.tsx @@ -27,6 +27,7 @@ import { ROUTES } from '../../../../utils/constants'; import { ChartContainer } from '../../../../components/Charts/ChartContainer'; import { getLogTypeLabel } from '../../../LogTypes/utils/helpers'; import { OverviewAlertItem, OverviewFindingItem } from '../../../../../types'; +import { getUseUpdatedUx } from '../../../../services/utils/constants'; export interface SummaryProps { findings: OverviewFindingItem[]; @@ -147,22 +148,24 @@ export const Summary: React.FC = ({ return ( - - {activeAlerts === 0 && totalFindings === 0 ? null : ( - - {createStatComponent( - 'Total active alerts', - { url: ROUTES.ALERTS, color: 'danger' }, - activeAlerts - )} - {createStatComponent( - 'Total findings', - { url: ROUTES.FINDINGS, color: 'primary' }, - totalFindings - )} - - )} - + {!getUseUpdatedUx() && ( + + {activeAlerts === 0 && totalFindings === 0 ? null : ( + + {createStatComponent( + 'Total active alerts', + { url: ROUTES.ALERTS, color: 'danger' }, + activeAlerts + )} + {createStatComponent( + 'Total findings', + { url: ROUTES.FINDINGS, color: 'primary' }, + totalFindings + )} + + )} + + )} {activeAlerts === 0 && totalFindings === 0 ? ( { return ( - + {children} diff --git a/public/pages/Overview/containers/Overview/Overview.tsx b/public/pages/Overview/containers/Overview/Overview.tsx index 09745a79a..cf88ead9e 100644 --- a/public/pages/Overview/containers/Overview/Overview.tsx +++ b/public/pages/Overview/containers/Overview/Overview.tsx @@ -13,6 +13,9 @@ import { EuiTitle, EuiSpacer, EuiSmallButton, + EuiCard, + EuiPanel, + EuiStat, } from '@elastic/eui'; import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { @@ -24,16 +27,18 @@ import { import { CoreServicesContext } from '../../../../../public/components/core_services'; import { RecentAlertsWidget } from '../../components/Widgets/RecentAlertsWidget'; import { RecentFindingsWidget } from '../../components/Widgets/RecentFindingsWidget'; -import { DetectorsWidget } from '../../components/Widgets/DetectorsWidget'; import { OverviewViewModelActor } from '../../models/OverviewViewModel'; import { SecurityAnalyticsContext } from '../../../../services'; import { Summary } from '../../components/Widgets/Summary'; import { TopRulesWidget } from '../../components/Widgets/TopRulesWidget'; -import { GettingStartedPopup } from '../../components/GettingStarted/GettingStartedPopup'; +import { GettingStartedContent } from '../../components/GettingStarted/GettingStartedContent'; import { getChartTimeUnit, TimeUnit } from '../../utils/helpers'; import { OverviewProps, OverviewState, OverviewViewModel } from '../../../../../types'; import { setBreadcrumbs } from '../../../../utils/helpers'; import { PageHeader } from '../../../../components/PageHeader/PageHeader'; +import { getOverviewStatsProps, getOverviewsCardsProps } from '../../utils/constants'; +import { getUseUpdatedUx } from '../../../../services/utils/constants'; +import { RecentThreatIntelFindingsWidget } from '../../components/Widgets/RecentThreatIntelFindingsWidget'; export const Overview: React.FC = (props) => { const { @@ -51,6 +56,8 @@ export const Overview: React.FC = (props) => { detectors: [], findings: [], alerts: [], + threatIntelFindings: [], + correlations: 0, }, }); @@ -74,7 +81,7 @@ export const Overview: React.FC = (props) => { return fireAbortSignals; }, [fireAbortSignals]); - const updateState = (overviewViewModel: OverviewViewModel, modelLoadingComplete: boolean) => { + const updateState = (overviewViewModel: OverviewViewModel, _modelLoadingComplete: boolean) => { setState({ ...state, overviewViewModel: { ...overviewViewModel }, @@ -219,10 +226,17 @@ export const Overview: React.FC = (props) => { anchorPosition="downRight" closePopover={closePopover} > - + ); + const overviewStats = { + alerts: state.overviewViewModel.alerts.filter((a) => !a.acknowledged).length, + correlations: state.overviewViewModel.correlations, + ruleFindings: state.overviewViewModel.findings.length, + threatIntelFindings: state.overviewViewModel.threatIntelFindings.length, + }; + return ( = (props) => { { renderComponent: datePicker }, { renderComponent: createDetectorAction }, ]} - appBadgeControls={[{ renderComponent: gettingStartedBadgeControl }]} > - +

Overview

@@ -246,6 +259,30 @@ export const Overview: React.FC = (props) => {
+ {getUseUpdatedUx() && ( + <> + + + {getOverviewsCardsProps().map((p, idx) => ( + + + + ))} + + + + + {getOverviewStatsProps(overviewStats).map((p, idx) => ( + + + + + + ))} + + + + )} = (props) => { - diff --git a/public/pages/Overview/models/OverviewViewModel.ts b/public/pages/Overview/models/OverviewViewModel.ts index 8f507cd94..d9d8397ae 100644 --- a/public/pages/Overview/models/OverviewViewModel.ts +++ b/public/pages/Overview/models/OverviewViewModel.ts @@ -18,6 +18,7 @@ import { OverviewFindingItem, OverviewViewModel, OverviewViewModelRefreshHandler, + ThreatIntelFinding, } from '../../../../types'; export class OverviewViewModelActor { @@ -25,6 +26,8 @@ export class OverviewViewModelActor { detectors: [], findings: [], alerts: [], + threatIntelFindings: [], + correlations: 0, }; private partialUpdateHandlers: OverviewViewModelRefreshHandler[] = []; private fullUpdateHandlers: OverviewViewModelRefreshHandler[] = []; @@ -65,12 +68,15 @@ export class OverviewViewModelActor { } private async updateFindings(signal: AbortSignal) { - const detectorInfo = new Map(); + const detectorInfo = new Map< + string, + { logType: string; name: string; detectorHit: DetectorHit } + >(); this.overviewViewModel.detectors.forEach((detectorHit) => { detectorInfo.set(detectorHit._id, { logType: detectorHit._source.detector_type, name: detectorHit._source.name, - detectorHit + detectorHit, }); }); const detectorIds = detectorInfo.keys(); @@ -78,8 +84,8 @@ export class OverviewViewModelActor { const ruleIds = new Set(); const duration = getDuration({ startTime: this.startTime, - endTime: this.endTime - }) + endTime: this.endTime, + }); try { for (let id of detectorIds) { @@ -139,8 +145,8 @@ export class OverviewViewModelActor { let alertItems: OverviewAlertItem[] = []; const duration = getDuration({ startTime: this.startTime, - endTime: this.endTime - }) + endTime: this.endTime, + }); try { for (let detector of this.overviewViewModel.detectors) { @@ -168,12 +174,55 @@ export class OverviewViewModelActor { this.overviewViewModel.alerts = this.filterChartDataByTime(alertItems); } + private async updateThreatIntelFindings(signal: AbortSignal) { + let tIFindings: ThreatIntelFinding[] = []; + const duration = getDuration({ + startTime: this.startTime, + endTime: this.endTime, + }); + + try { + tIFindings = await DataStore.threatIntel.getAllThreatIntelFindings(signal, duration); + } catch (e: any) { + errorNotificationToast(this.notifications, 'retrieve', 'threat intel findings', e); + } + + this.overviewViewModel.threatIntelFindings = this.filterChartDataByTime( + tIFindings, + 'timestamp' + ); + } + + private async updateCorrelationsCount() { + let count = 0; + const duration = getDuration({ + startTime: this.startTime, + endTime: this.endTime, + }); + + try { + count = await DataStore.correlations.getCorrelationsCountInWindow( + duration.startTime.toString(), + duration.endTime.toString() + ); + } catch (e: any) { + errorNotificationToast(this.notifications, 'retrieve', 'correlation count', e); + } + + this.overviewViewModel.correlations = count; + } + public getOverviewViewModel() { return this.overviewViewModel; } - public registerRefreshHandler(handler: OverviewViewModelRefreshHandler, allowPartialResults: boolean) { - allowPartialResults ? this.partialUpdateHandlers.push(handler) : this.fullUpdateHandlers.push(handler); + public registerRefreshHandler( + handler: OverviewViewModelRefreshHandler, + allowPartialResults: boolean + ) { + allowPartialResults + ? this.partialUpdateHandlers.push(handler) + : this.fullUpdateHandlers.push(handler); } startTime = DEFAULT_DATE_RANGE.start; @@ -189,34 +238,48 @@ export class OverviewViewModelActor { this.refreshState = 'InProgress'; - await this.runSteps([ - async () => { - await this.updateDetectors(); - this.updateResults(this.partialUpdateHandlers, false); - }, - async () => { - await this.updateFindings(signal); - this.updateResults(this.partialUpdateHandlers, false); - }, - async (signal: AbortSignal) => { - await this.updateAlerts(signal); - this.updateResults(this.partialUpdateHandlers, false); - } - ], signal); + await this.runSteps( + [ + async () => { + await this.updateDetectors(); + this.updateResults(this.partialUpdateHandlers, false); + }, + async (signal: AbortSignal) => { + await this.updateFindings(signal); + this.updateResults(this.partialUpdateHandlers, false); + }, + async (signal: AbortSignal) => { + await this.updateAlerts(signal); + this.updateResults(this.partialUpdateHandlers, false); + }, + async (signal: AbortSignal) => { + await this.updateThreatIntelFindings(signal); + this.updateResults(this.partialUpdateHandlers, false); + }, + async (_signal: AbortSignal) => { + await this.updateCorrelationsCount(); + this.updateResults(this.partialUpdateHandlers, false); + }, + ], + signal + ); this.updateResults(this.fullUpdateHandlers, true); this.refreshState = 'Complete'; } - private filterChartDataByTime = (chartData: any) => { + private filterChartDataByTime = (chartData: any, timeField: string = 'time') => { const startMoment = dateMath.parse(this.startTime); const endMoment = dateMath.parse(this.endTime); return chartData.filter((dataItem: any) => { - return moment(dataItem.time).isBetween(moment(startMoment), moment(endMoment)); + return moment(dataItem[timeField]).isBetween(moment(startMoment), moment(endMoment)); }); }; - private updateResults(handlers: OverviewViewModelRefreshHandler[], modelLoadingComplete: boolean) { + private updateResults( + handlers: OverviewViewModelRefreshHandler[], + modelLoadingComplete: boolean + ) { handlers.forEach((handler) => { handler(this.overviewViewModel, modelLoadingComplete); }); @@ -227,7 +290,7 @@ export class OverviewViewModelActor { if (signal.aborted) { break; } - + await step(signal); if (signal.aborted) { diff --git a/public/pages/Overview/utils/constants.ts b/public/pages/Overview/utils/constants.ts index d8438e667..f3e51317e 100644 --- a/public/pages/Overview/utils/constants.ts +++ b/public/pages/Overview/utils/constants.ts @@ -3,9 +3,114 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { EuiCardProps, EuiStatProps } from '@elastic/eui'; +import { + CORRELATIONS_RULE_NAV_ID, + DETECTORS_NAV_ID, + GETTING_STARTED_NAV_ID, + THREAT_ALERTS_NAV_ID, + THREAT_INTEL_NAV_ID, +} from '../../../utils/constants'; +import { getApplication } from '../../../services/utils/constants'; + export const summaryGroupByOptions = [ { text: 'All findings', value: 'finding' }, { text: 'Log type', value: 'logType' }, ]; export const moreLink = 'https://opensearch.org/docs/latest/security-analytics/'; + +export const getOverviewsCardsProps = (): EuiCardProps[] => [ + { + title: 'Configure Security Analytics', + description: 'Set up tools and components to get started.', + selectable: { + onClick: () => { + getApplication().navigateToApp(GETTING_STARTED_NAV_ID); + }, + children: 'Getting started guide', + isDisabled: false, + }, + }, + { + title: 'Uncover security findings', + description: 'Identify security threats in your log data with detection rules.', + selectable: { + onClick: () => { + getApplication().navigateToApp(DETECTORS_NAV_ID); + }, + children: 'Threat detectors', + isDisabled: false, + }, + }, + { + title: 'Discover insights', + description: 'Explore data to uncover insights.', + selectable: { + onClick: () => { + getApplication().navigateToApp('discover'); + }, + children: 'Discover', + isDisabled: false, + }, + }, + { + title: 'Get notified', + description: 'Receive timely notifications with detector-driven alerts.', + selectable: { + onClick: () => { + getApplication().navigateToApp(THREAT_ALERTS_NAV_ID); + }, + children: 'Threat alerts', + isDisabled: false, + }, + }, + { + title: 'Correlate events', + description: 'Detect multi-system threats with correlation rule builder', + selectable: { + onClick: () => { + getApplication().navigateToApp(CORRELATIONS_RULE_NAV_ID); + }, + children: 'Correlation rules', + isDisabled: false, + }, + }, + { + title: 'Scan your logs', + description: 'Identify malicious actors from known indicators of compromise.', + selectable: { + onClick: () => { + getApplication().navigateToApp(THREAT_INTEL_NAV_ID); + }, + children: ' Threat intelligence', + isDisabled: false, + }, + }, +]; + +export const getOverviewStatsProps = ({ + alerts, + correlations, + ruleFindings, + threatIntelFindings, +}: any): EuiStatProps[] => { + return [ + { + title: alerts, + description: 'Total active alerts', + }, + { + title: correlations, + description: 'Correlations', + }, + { + title: ruleFindings, + description: 'Detection rule findings', + }, + { + title: threatIntelFindings, + description: 'Threat intel findings', + }, + ]; +}; diff --git a/public/pages/Rules/components/RuleEditor/RuleEditorForm.tsx b/public/pages/Rules/components/RuleEditor/RuleEditorForm.tsx index d5976b299..25f59618e 100644 --- a/public/pages/Rules/components/RuleEditor/RuleEditorForm.tsx +++ b/public/pages/Rules/components/RuleEditor/RuleEditorForm.tsx @@ -44,6 +44,7 @@ import { getLogTypeLabel } from '../../../LogTypes/utils/helpers'; import { getSeverityLabel } from '../../../Correlations/utils/constants'; import { DataSourceContext } from '../../../../services/DataSourceContext'; import { PageHeader } from '../../../../components/PageHeader/PageHeader'; +import { TopNavControlLinkData } from '../../../../../../../src/plugins/navigation/public'; export interface VisualRuleEditorProps { initialValue: RuleEditorFormModel; @@ -53,7 +54,7 @@ export interface VisualRuleEditorProps { cancel: () => void; mode: 'create' | 'edit'; title: string; - subtitleData?: { text: string; href?: string }[]; + subtitleData?: { description: string; links?: TopNavControlLinkData }; } const editorTypes = [ @@ -195,45 +196,22 @@ export const RuleEditorForm: React.FC = ({ return (
- { - return slice.href - ? { - label: slice.text, - href: slice.href, - controlType: 'link', - target: '_blank', - iconType: 'popout', - iconSide: 'right', - } - : { - description: slice.text, - }; - }) - : undefined - } - > +

{title}

- {subtitleData && - subtitleData.map((slice, idx) => { - return ( - - {slice.href ? ( - - {slice.text} - - ) : ( - - {slice.text} - - )} - - ); - })} + {subtitleData && ( + <> + + {subtitleData.description} + + {subtitleData.links && ( + + {subtitleData.links.label} + + )} + + )}
= ({ history, services, notif return ( = ({ { description: `Scan log data for indicators of compromise from threat intel data streams to identify malicious actors and security threats.`, - }, - { - label: 'Learn more', - href: - 'https://opensearch.org/docs/latest/security-analytics/threat-intelligence/index/', - controlType: 'link', - target: '_blank', - iconType: 'popout', - iconSide: 'right', + links: [ + { + label: 'Learn more', + href: + 'https://opensearch.org/docs/latest/security-analytics/threat-intelligence/index/', + controlType: 'link', + target: '_blank', + flush: 'both', + }, + ], }, ]} > diff --git a/public/pages/ThreatIntel/utils/helpers.ts b/public/pages/ThreatIntel/utils/helpers.ts index ecbc87eb0..8669cfa91 100644 --- a/public/pages/ThreatIntel/utils/helpers.ts +++ b/public/pages/ThreatIntel/utils/helpers.ts @@ -132,6 +132,7 @@ export function getEmptyThreatIntelSourcePayloadBase(): ThreatIntelSourcePayload store_type: 'OS', enabled: true, ioc_types: [], + enabled_for_scan: true, }; } @@ -260,7 +261,7 @@ export function readIocsFromFile( export function threatIntelSourceItemToBasePayload( sourceItem: ThreatIntelSourceItem ): ThreatIntelSourcePayloadBase { - const { name, description, enabled, ioc_types } = sourceItem; + const { name, description, enabled, ioc_types, enabled_for_scan } = sourceItem; return { name, description, @@ -268,5 +269,6 @@ export function threatIntelSourceItemToBasePayload( store_type: 'OS', enabled, ioc_types, + enabled_for_scan, }; } diff --git a/public/plugin.ts b/public/plugin.ts index 9fa1b8829..54a74f43c 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -17,8 +17,9 @@ import { CORRELATIONS_NAV_ID, CORRELATIONS_RULE_NAV_ID, DETECTORS_NAV_ID, - DETECTORS_RULE_NAV_ID, + DETECTION_RULE_NAV_ID, FINDINGS_NAV_ID, + GETTING_STARTED_NAV_ID, LOG_TYPES_NAV_ID, OVERVIEW_NAV_ID, PLUGIN_NAME, @@ -73,13 +74,15 @@ export class SecurityAnalyticsPlugin ) {} private updateDefaultRouteOfManagementApplications: AppUpdater = () => { - const hash = `#/?dataSourceId=${dataSourceObservable.value?.id || ""}`; + const hash = `#/?dataSourceId=${dataSourceObservable.value?.id || ''}`; return { defaultPath: hash, }; }; - private appStateUpdater = new BehaviorSubject(this.updateDefaultRouteOfManagementApplications); + private appStateUpdater = new BehaviorSubject( + this.updateDefaultRouteOfManagementApplications + ); public setup( core: CoreSetup, @@ -118,6 +121,16 @@ export class SecurityAnalyticsPlugin }, }); + core.application.register({ + id: GETTING_STARTED_NAV_ID, + title: 'Getting started', + order: 1, + updater$: this.appStateUpdater, + mount: async (params: AppMountParameters) => { + return mountWrapper(params, ROUTES.GETTING_STARTED); + }, + }); + core.application.register({ id: THREAT_ALERTS_NAV_ID, title: 'Threat alerts', @@ -163,7 +176,7 @@ export class SecurityAnalyticsPlugin }); core.application.register({ - id: DETECTORS_RULE_NAV_ID, + id: DETECTION_RULE_NAV_ID, title: 'Detection rules', order: 9080, category: DEFAULT_APP_CATEGORIES.configure, @@ -214,12 +227,13 @@ export class SecurityAnalyticsPlugin const navlinks = [ { id: OVERVIEW_NAV_ID, showInAllNavGroup: true }, + { id: GETTING_STARTED_NAV_ID, showInAllNavGroup: true }, { id: THREAT_ALERTS_NAV_ID, showInAllNavGroup: true }, { id: FINDINGS_NAV_ID, showInAllNavGroup: true }, { id: CORRELATIONS_NAV_ID }, { id: PLUGIN_NAME, category: DEFAULT_APP_CATEGORIES.configure, title: 'Threat detection' }, { id: DETECTORS_NAV_ID, parentNavLinkId: PLUGIN_NAME }, - { id: DETECTORS_RULE_NAV_ID, parentNavLinkId: PLUGIN_NAME }, + { id: DETECTION_RULE_NAV_ID, parentNavLinkId: PLUGIN_NAME }, { id: CORRELATIONS_RULE_NAV_ID }, { id: THREAT_INTEL_NAV_ID }, { id: LOG_TYPES_NAV_ID }, diff --git a/public/store/CorrelationsStore.ts b/public/store/CorrelationsStore.ts index 4c0e81f94..b117d97b1 100644 --- a/public/store/CorrelationsStore.ts +++ b/public/store/CorrelationsStore.ts @@ -190,6 +190,19 @@ export class CorrelationsStore implements ICorrelationsStore { return response.ok; } + public async getCorrelationsCountInWindow(start_time: string, end_time: string): Promise { + const allCorrelationsRes = await this.service.getAllCorrelationsInTimeWindow( + start_time, + end_time + ); + + if (allCorrelationsRes.ok) { + return allCorrelationsRes.response.findings.length; + } + + return 0; + } + public async getAllCorrelationsInWindow( start_time: string, end_time: string diff --git a/public/utils/constants.ts b/public/utils/constants.ts index de2cf0b5f..c3355678d 100644 --- a/public/utils/constants.ts +++ b/public/utils/constants.ts @@ -11,7 +11,7 @@ import _ from 'lodash'; import { euiPaletteColorBlind, euiPaletteForStatus } from '@elastic/eui'; import { DataSourceOption } from 'src/plugins/data_source_management/public'; import { BehaviorSubject } from 'rxjs'; -import { i18n } from "@osd/i18n"; +import { i18n } from '@osd/i18n'; export const DATE_MATH_FORMAT = 'YYYY-MM-DDTHH:mm:ss.SSSZ'; export const MAX_RECENTLY_USED_TIME_RANGES = 5; @@ -22,12 +22,13 @@ export const PLUGIN_NAME = 'opensearch_security_analytics_dashboards'; export const OS_NOTIFICATION_PLUGIN = 'opensearch-notifications'; export const DEFAULT_EMPTY_DATA = '-'; -export const OVERVIEW_NAV_ID = `overview`; +export const OVERVIEW_NAV_ID = `sa_overview`; +export const GETTING_STARTED_NAV_ID = `getting_started`; export const THREAT_ALERTS_NAV_ID = `threat_alerts`; export const FINDINGS_NAV_ID = `findings`; export const CORRELATIONS_NAV_ID = `correlations`; export const DETECTORS_NAV_ID = `detectors`; -export const DETECTORS_RULE_NAV_ID = `detectors_rules`; +export const DETECTION_RULE_NAV_ID = `detection_rules`; export const CORRELATIONS_RULE_NAV_ID = `correlation_rules`; export const THREAT_INTEL_NAV_ID = `threat_intelligence`; export const LOG_TYPES_NAV_ID = `log_types`; @@ -44,6 +45,7 @@ export const ROUTES = Object.freeze({ DETECTORS: '/detectors', FINDINGS: '/findings', OVERVIEW: '/overview', + GETTING_STARTED: '/getting-started', RULES: '/rules', RULES_CREATE: '/create-rule', RULES_EDIT: '/edit-rule', @@ -79,6 +81,7 @@ export const getNotificationDetailsHref = (channelId: string) => export const BREADCRUMBS = Object.freeze({ SECURITY_ANALYTICS: { text: 'Security Analytics', href: '#/' }, OVERVIEW: { text: 'Overview', href: `#${ROUTES.OVERVIEW}` }, + GETTING_STARTED: { text: 'Getting started', href: `#${ROUTES.GETTING_STARTED}` }, FINDINGS: { text: 'Findings', href: `#${ROUTES.FINDINGS}` }, DETECTORS: { text: 'Threat detectors', href: `#${ROUTES.DETECTORS}` }, DETECTORS_CREATE: { text: 'Create detector', href: `#${ROUTES.DETECTORS_CREATE}` }, @@ -256,48 +259,61 @@ export const ALERT_SEVERITY_OPTIONS = { value: '1', label: '1 (Highest)', text: '1 (Highest)', - badgeLabel: 'Highest', - color: { background: paletteColors[4], text: 'white' }, }, HIGH: { id: '2', value: '2', label: '2 (High)', text: '2 (High)', - badgeLabel: 'High', - color: { background: paletteColors[3], text: 'white' }, }, MEDIUM: { id: '3', value: '3', label: '3 (Medium)', text: '3 (Medium)', - badgeLabel: 'Medium', - color: { background: paletteColors[2], text: 'black' }, }, LOW: { id: '4', value: '4', label: '4 (Low)', text: '4 (Low)', - badgeLabel: 'Low', - color: { background: paletteColors[1], text: 'white' }, }, LOWEST: { id: '5', value: '5', label: '5 (Lowest)', text: '5 (Lowest)', + }, +}; + +export const ALERT_SEVERITY_PROPS = { + [ALERT_SEVERITY_OPTIONS.HIGHEST.value]: { + badgeLabel: 'Highest', + color: { background: paletteColors[4], text: 'white' }, + }, + [ALERT_SEVERITY_OPTIONS.HIGH.value]: { + badgeLabel: 'High', + color: { background: paletteColors[3], text: 'white' }, + }, + [ALERT_SEVERITY_OPTIONS.MEDIUM.value]: { + badgeLabel: 'Medium', + color: { background: paletteColors[2], text: 'black' }, + }, + [ALERT_SEVERITY_OPTIONS.LOW.value]: { + badgeLabel: 'Low', + color: { background: paletteColors[1], text: 'white' }, + }, + [ALERT_SEVERITY_OPTIONS.LOWEST.value]: { badgeLabel: 'Lowest', color: { background: paletteColors[0], text: 'white' }, }, }; const LocalCluster: DataSourceOption = { - label: i18n.translate("dataSource.localCluster", { - defaultMessage: "Local cluster", + label: i18n.translate('dataSource.localCluster', { + defaultMessage: 'Local cluster', }), - id: "", + id: '', }; export const dataSourceObservable = new BehaviorSubject(LocalCluster); diff --git a/public/utils/helpers.tsx b/public/utils/helpers.tsx index c7b943609..dca64012e 100644 --- a/public/utils/helpers.tsx +++ b/public/utils/helpers.tsx @@ -20,6 +20,7 @@ import { PeriodSchedule } from '../../models/interfaces'; import React from 'react'; import { ALERT_SEVERITY_OPTIONS, + ALERT_SEVERITY_PROPS, BREADCRUMBS, DEFAULT_EMPTY_DATA, defaultColorForVisualizations, @@ -33,7 +34,12 @@ import { RuleItemInfo, } from '../pages/CreateDetector/components/DefineDetector/components/DetectionRules/types/interfaces'; import { RuleInfo } from '../../server/models/interfaces'; -import { ChromeBreadcrumb, CoreStart, NotificationsStart } from 'opensearch-dashboards/public'; +import { + ChromeBreadcrumb, + CoreStart, + NotificationsStart, + SavedObject, +} from 'opensearch-dashboards/public'; import { AlertsService, FieldMappingService, @@ -77,6 +83,7 @@ import { IndexPatternsService as CoreIndexPatternsService } from '../../../../sr import semver from 'semver'; import * as pluginManifest from '../../opensearch_dashboards.json'; import { DataSourceThreatAlertsCard } from '../components/DataSourceThreatAlertsCard/DataSourceThreatAlertsCard'; +import { DataSourceAttributes } from '../../../../src/plugins/data_source/common/data_sources'; export const parseStringsToOptions = (strings: string[]) => { return strings.map((str) => ({ id: str, label: str })); @@ -377,7 +384,7 @@ export const getSeverityBadge = (severity: string) => { const severityLevel = ruleSeverity.find((sev) => sev.value === severity); return ( - {severity || DEFAULT_EMPTY_DATA} + {severityLevel?.name || DEFAULT_EMPTY_DATA} ); }; @@ -643,7 +650,7 @@ export function setBreadcrumbs(crumbs: ChromeBreadcrumb[]) { getBreadCrumbsSetter()(getUseUpdatedUx() ? crumbs : [BREADCRUMBS.SECURITY_ANALYTICS, ...crumbs]); } -export function dataSourceFilterFn(dataSource) { +export function dataSourceFilterFn(dataSource: SavedObject) { const dataSourceVersion = dataSource?.attributes?.dataSourceVersion || ''; const installedPlugins = dataSource?.attributes?.installedPlugins || []; return ( @@ -652,19 +659,31 @@ export function dataSourceFilterFn(dataSource) { ); } -export function getSeverityText(severity) { +export function getSeverityText(severity: string) { return _.get(_.find(ALERT_SEVERITY_OPTIONS, { value: severity }), 'text'); } -export function getBadgeText(severity) { - return _.get(_.find(ALERT_SEVERITY_OPTIONS, { value: severity }), 'badgeLabel'); +export function getBadgeText(severity: string) { + return ALERT_SEVERITY_PROPS[severity]?.badgeLabel || DEFAULT_EMPTY_DATA; +} + +export function getAlertSeverityColor(severity: string) { + return ALERT_SEVERITY_PROPS[severity]?.color || { background: 'white', text: 'black' }; } -export function getSeverityColor(severity) { - return _.get(_.find(ALERT_SEVERITY_OPTIONS, { value: severity }), 'color'); +export function getAlertSeverityBadge(severity: string) { + const severityColor = getAlertSeverityColor(severity); + return ( + + {getBadgeText(severity)} + + ); } -export const getTruncatedText = (text, textLength = 14) => { +export const getTruncatedText = (text: string, textLength: number = 14) => { return `${text.slice(0, textLength)}${text.length > textLength ? '...' : ''}`; }; diff --git a/types/Correlations.ts b/types/Correlations.ts index 0e35c25ef..1bb331893 100644 --- a/types/Correlations.ts +++ b/types/Correlations.ts @@ -149,6 +149,7 @@ export interface ICorrelationsStore { createCorrelationRule(correlationRule: CorrelationRule): void; updateCorrelationRule(correlationRule: CorrelationRule): void; deleteCorrelationRule(ruleId: string): Promise; + getCorrelationsCountInWindow(start_time: string, end_time: string): Promise; getAllCorrelationsInWindow( start_time: string, end_time: string diff --git a/types/Overview.ts b/types/Overview.ts index a0035f4c2..6a0ba1ca3 100644 --- a/types/Overview.ts +++ b/types/Overview.ts @@ -9,6 +9,7 @@ import { RouteComponentProps } from 'react-router-dom'; import { DetectorHit } from './Detector'; import { SortDirection } from '../public/utils/constants'; import { DataSourceProps } from './DataSource'; +import { ThreatIntelFinding } from './ThreatIntel'; export interface DateTimeFilter { startTime: string; @@ -19,9 +20,14 @@ export interface OverviewViewModel { detectors: DetectorHit[]; findings: OverviewFindingItem[]; alerts: OverviewAlertItem[]; + threatIntelFindings: ThreatIntelFinding[]; + correlations: number; } -export type OverviewViewModelRefreshHandler = (overviewState: OverviewViewModel, modelUpdateComplete: boolean) => void; +export type OverviewViewModelRefreshHandler = ( + overviewState: OverviewViewModel, + modelUpdateComplete: boolean +) => void; export interface OverviewProps extends RouteComponentProps, DataSourceProps { getStartedDismissedOnce: boolean; @@ -64,7 +70,11 @@ export interface OverviewDetectorItem { logTypes: string; } -export type TableWidgetItem = OverviewFindingItem | OverviewAlertItem | OverviewDetectorItem; +export type TableWidgetItem = + | OverviewFindingItem + | OverviewAlertItem + | OverviewDetectorItem + | ThreatIntelFinding; export type TableWidgetProps = { columns: EuiBasicTableColumn[];