Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.6] [SIEM] Detections add alert & signal tab (#55127) #55264

Merged
merged 1 commit into from
Jan 18, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -16,11 +16,11 @@ import { MatrixHistogramGqlQuery } from '../../containers/matrix_histogram/index
const ID = 'alertsOverTimeQuery';
export const alertsStackByOptions: MatrixHistogramOption[] = [
{
text: i18n.CATEGORY,
text: 'event.category',
value: 'event.category',
},
{
text: i18n.MODULE,
text: 'event.module',
value: 'event.module',
},
];
@@ -54,7 +54,6 @@ export const AlertsView = ({
<>
<MatrixHistogramContainer
dataKey={dataKey}
deleteQuery={deleteQuery}
defaultStackByOption={alertsStackByOptions[1]}
endDate={endDate}
errorMessage={i18n.ERROR_FETCHING_ALERTS_DATA}
@@ -68,7 +67,7 @@ export const AlertsView = ({
stackByOptions={alertsStackByOptions}
startDate={startDate}
subtitle={getSubtitle}
title={i18n.ALERTS_DOCUMENT_TYPE}
title={i18n.ALERTS_GRAPH_TITLE}
type={type}
updateDateRange={updateDateRange}
/>
Original file line number Diff line number Diff line change
@@ -14,10 +14,14 @@ export const TOTAL_COUNT_OF_ALERTS = i18n.translate('xpack.siem.alertsView.total
defaultMessage: 'alerts match the search criteria',
});

export const ALERTS_TABLE_TITLE = i18n.translate('xpack.siem.alertsView.alertsDocumentType', {
export const ALERTS_TABLE_TITLE = i18n.translate('xpack.siem.alertsView.alertsTableTitle', {
defaultMessage: 'Alerts',
});

export const ALERTS_GRAPH_TITLE = i18n.translate('xpack.siem.alertsView.alertsGraphTitle', {
defaultMessage: 'Alert detection frequency',
});

export const ALERTS_STACK_BY_MODULE = i18n.translate(
'xpack.siem.alertsView.alertsStackByOptions.module',
{
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@ import { RedirectToHostsPage, RedirectToHostDetailsPage } from './redirect_to_ho
import { RedirectToNetworkPage } from './redirect_to_network';
import { RedirectToOverviewPage } from './redirect_to_overview';
import { RedirectToTimelinesPage } from './redirect_to_timelines';
import { DetectionEngineTab } from '../../pages/detection_engine/types';

interface LinkToPageProps {
match: RouteMatch<{}>;
@@ -63,6 +64,12 @@ export const LinkToPage = React.memo<LinkToPageProps>(({ match }) => (
path={`${match.url}/:pageName(${SiemPageName.detectionEngine})`}
strict
/>
<Route
component={RedirectToDetectionEnginePage}
exact
path={`${match.url}/:pageName(${SiemPageName.detectionEngine})/:tabName(${DetectionEngineTab.alerts}|${DetectionEngineTab.signals})`}
strict
/>
<Route
component={RedirectToRulesPage}
exact
Original file line number Diff line number Diff line change
@@ -7,19 +7,28 @@
import React from 'react';
import { RouteComponentProps } from 'react-router-dom';

import { DetectionEngineTab } from '../../pages/detection_engine/types';
import { RedirectWrapper } from './redirect_wrapper';

export type DetectionEngineComponentProps = RouteComponentProps<{
tabName: DetectionEngineTab;
search: string;
}>;

export const DETECTION_ENGINE_PAGE_NAME = 'detection-engine';

export const RedirectToDetectionEnginePage = ({
match: {
params: { tabName },
},
location: { search },
}: DetectionEngineComponentProps) => (
<RedirectWrapper to={`/${DETECTION_ENGINE_PAGE_NAME}${search}`} />
);
}: DetectionEngineComponentProps) => {
const defaultSelectedTab = DetectionEngineTab.signals;
const selectedTab = tabName ? tabName : defaultSelectedTab;
const to = `/${DETECTION_ENGINE_PAGE_NAME}/${selectedTab}${search}`;

return <RedirectWrapper to={to} />;
};

export const RedirectToRulesPage = ({ location: { search } }: DetectionEngineComponentProps) => {
return <RedirectWrapper to={`/${DETECTION_ENGINE_PAGE_NAME}/rules${search}`} />;
@@ -28,7 +37,7 @@ export const RedirectToRulesPage = ({ location: { search } }: DetectionEngineCom
export const RedirectToCreateRulePage = ({
location: { search },
}: DetectionEngineComponentProps) => {
return <RedirectWrapper to={`/${DETECTION_ENGINE_PAGE_NAME}/rules/create-rule${search}`} />;
return <RedirectWrapper to={`/${DETECTION_ENGINE_PAGE_NAME}/rules/create${search}`} />;
};

export const RedirectToRuleDetailsPage = ({
@@ -44,6 +53,8 @@ export const RedirectToEditRulePage = ({ location: { search } }: DetectionEngine
};

export const getDetectionEngineUrl = () => `#/link-to/${DETECTION_ENGINE_PAGE_NAME}`;
export const getDetectionEngineAlertUrl = () =>
`#/link-to/${DETECTION_ENGINE_PAGE_NAME}/${DetectionEngineTab.alerts}`;
export const getRulesUrl = () => `#/link-to/${DETECTION_ENGINE_PAGE_NAME}/rules`;
export const getCreateRuleUrl = () => `#/link-to/${DETECTION_ENGINE_PAGE_NAME}/rules/create-rule`;
export const getRuleDetailsUrl = () => `#/link-to/${DETECTION_ENGINE_PAGE_NAME}/rules/rule-details`;
Original file line number Diff line number Diff line change
@@ -46,12 +46,12 @@ export const MatrixHistogramComponent: React.FC<MatrixHistogramProps &
isDnsHistogram,
isEventsHistogram,
isInspected,
legendPosition,
legendPosition = 'right',
mapping,
query,
scaleType = ScaleType.Time,
setQuery,
showLegend,
showLegend = true,
skip,
stackByOptions,
startDate,
@@ -151,6 +151,7 @@ export const MatrixHistogramComponent: React.FC<MatrixHistogramProps &
isInspected,
loading,
data,
refetch,
]);

return !hideHistogram ? (
Original file line number Diff line number Diff line change
@@ -39,6 +39,14 @@ export const AnomaliesQueryTabBody = ({
flowTarget,
ip,
}: AnomaliesQueryTabBodyProps) => {
useEffect(() => {
return () => {
if (deleteQuery) {
deleteQuery({ id: ID });
}
};
}, []);

const [, siemJobs] = useSiemJobs(true);
const [anomalyScore] = useUiSetting$<number>(DEFAULT_ANOMALY_SCORE);

@@ -51,21 +59,12 @@ export const AnomaliesQueryTabBody = ({
ip
);

useEffect(() => {
return () => {
if (deleteQuery) {
deleteQuery({ id: ID });
}
};
}, []);

return (
<>
<MatrixHistogramContainer
isAnomaliesHistogram={true}
dataKey="AnomaliesHistogram"
defaultStackByOption={anomaliesStackByOptions[0]}
deleteQuery={deleteQuery}
endDate={endDate}
errorMessage={i18n.ERROR_FETCHING_ANOMALIES_DATA}
filterQuery={mergedFilterQuery}
Original file line number Diff line number Diff line change
@@ -26,7 +26,6 @@ import { SetQuery } from '../../pages/hosts/navigation/types';
export interface OwnProps extends QueryTemplateProps {
dataKey: string | string[];
defaultStackByOption: MatrixHistogramOption;
deleteQuery?: ({ id }: { id: string }) => void;
errorMessage: string;
headerChildren?: React.ReactNode;
hideHistogramIfEmpty?: boolean;
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { getOr } from 'lodash/fp';
import { useEffect, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import {
MatrixHistogramDataTypes,
MatrixHistogramQueryProps,
@@ -35,7 +35,7 @@ export const useQuery = <Hit, Aggs, TCache = object>({
}: MatrixHistogramQueryProps) => {
const [defaultIndex] = useUiSetting$<string[]>(DEFAULT_INDEX_KEY);
const [, dispatchToaster] = useStateToaster();
const [refetch, setRefetch] = useState<inputsModel.Refetch>();
const refetch = useRef<inputsModel.Refetch>();
const [loading, setLoading] = useState<boolean>(false);
const [data, setData] = useState<MatrixHistogramDataTypes[] | null>(null);
const [inspect, setInspect] = useState<inputsModel.InspectQuery | null>(null);
@@ -71,7 +71,7 @@ export const useQuery = <Hit, Aggs, TCache = object>({
return apolloClient
.query<GetMatrixHistogramQuery.Query, GetMatrixHistogramQuery.Variables>({
query,
fetchPolicy: 'cache-first',
fetchPolicy: 'network-only',
variables: matrixHistogramVariables,
context: {
fetchOptions: {
@@ -103,9 +103,7 @@ export const useQuery = <Hit, Aggs, TCache = object>({
}
);
}
setRefetch(() => {
fetchData();
});
refetch.current = fetchData;
fetchData();
return () => {
isSubscribed = false;
@@ -122,5 +120,5 @@ export const useQuery = <Hit, Aggs, TCache = object>({
endDate,
]);

return { data, loading, inspect, totalCount, refetch };
return { data, loading, inspect, totalCount, refetch: refetch.current };
};
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ export const PAGE_TITLE = i18n.translate('xpack.siem.detectionEngine.pageTitle',
});

export const SIGNALS_TABLE_TITLE = i18n.translate('xpack.siem.detectionEngine.signals.tableTitle', {
defaultMessage: 'All signals',
defaultMessage: 'Signals',
});

export const SIGNALS_DOCUMENT_TYPE = i18n.translate(
Original file line number Diff line number Diff line change
@@ -4,18 +4,17 @@
* you may not use this file except in compliance with the Elastic License.
*/

import * as i18n from './translations';
import { SignalsHistogramOption } from './types';

export const signalsHistogramOptions: SignalsHistogramOption[] = [
{ text: i18n.STACK_BY_RISK_SCORES, value: 'signal.rule.risk_score' },
{ text: i18n.STACK_BY_SEVERITIES, value: 'signal.rule.severity' },
{ text: i18n.STACK_BY_DESTINATION_IPS, value: 'destination.ip' },
{ text: i18n.STACK_BY_ACTIONS, value: 'event.action' },
{ text: i18n.STACK_BY_CATEGORIES, value: 'event.category' },
{ text: i18n.STACK_BY_HOST_NAMES, value: 'host.name' },
{ text: i18n.STACK_BY_RULE_TYPES, value: 'signal.rule.type' },
{ text: i18n.STACK_BY_RULE_NAMES, value: 'signal.rule.name' },
{ text: i18n.STACK_BY_SOURCE_IPS, value: 'source.ip' },
{ text: i18n.STACK_BY_USERS, value: 'user.name' },
{ text: 'signal.rule.risk_score', value: 'signal.rule.risk_score' },
{ text: 'signal.rule.severity', value: 'signal.rule.severity' },
{ text: 'destination.ip', value: 'destination.ip' },
{ text: 'event.action', value: 'event.action' },
{ text: 'event.category', value: 'event.category' },
{ text: 'host.name', value: 'host.name' },
{ text: 'signal.rule.type', value: 'signal.rule.type' },
{ text: 'signal.rule.name', value: 'signal.rule.name' },
{ text: 'source.ip', value: 'source.ip' },
{ text: 'user.name', value: 'user.name' },
];
Original file line number Diff line number Diff line change
@@ -46,7 +46,7 @@ export const SignalsHistogramPanel = memo<SignalsHistogramPanelProps>(
filters,
query,
from,
legendPosition = 'bottom',
legendPosition = 'right',
loadingInitial = false,
showLinkToSignals = false,
showTotalSignalsCount = false,
Original file line number Diff line number Diff line change
@@ -44,7 +44,7 @@ export const SignalsHistogram = React.memo<HistogramSignalsProps>(
from,
query,
filters,
legendPosition = 'bottom',
legendPosition = 'right',
loadingInitial,
setTotalSignalsCount,
stackByField,
Original file line number Diff line number Diff line change
@@ -4,27 +4,31 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { EuiButton, EuiSpacer } from '@elastic/eui';
import React, { useCallback } from 'react';
import { EuiButton, EuiSpacer, EuiTab, EuiTabs } from '@elastic/eui';
import React, { useCallback, useMemo } from 'react';
import { useParams } from 'react-router-dom';
import { StickyContainer } from 'react-sticky';

import { connect } from 'react-redux';
import { ActionCreator } from 'typescript-fsa';

import { Query } from '../../../../../../../src/plugins/data/common/query';
import { esFilters } from '../../../../../../../src/plugins/data/common/es_query';

import { GlobalTime } from '../../containers/global_time';
import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source';
import { AlertsTable } from '../../components/alerts_viewer/alerts_table';
import { FiltersGlobal } from '../../components/filters_global';
import { HeaderPage } from '../../components/header_page';
import { DETECTION_ENGINE_PAGE_NAME } from '../../components/link_to/redirect_to_detection_engine';
import { SiemSearchBar } from '../../components/search_bar';
import { WrapperPage } from '../../components/wrapper_page';
import { GlobalTime } from '../../containers/global_time';
import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source';
import { SpyRoute } from '../../utils/route/spy_routes';

import { Query } from '../../../../../../../src/plugins/data/common/query';
import { esFilters } from '../../../../../../../src/plugins/data/common/es_query';
import { State } from '../../store';
import { inputsSelectors } from '../../store/inputs';
import { setAbsoluteRangeDatePicker as dispatchSetAbsoluteRangeDatePicker } from '../../store/inputs/actions';
import { SpyRoute } from '../../utils/route/spy_routes';
import { InputsModelId } from '../../store/inputs/constants';
import { InputsRange } from '../../store/inputs/model';
import { AlertsByCategory } from '../overview/alerts_by_category';
import { useSignalInfo } from './components/signals_info';
import { SignalsTable } from './components/signals';
import { NoWriteSignalsCallOut } from './components/no_write_signals_callout';
@@ -35,6 +39,7 @@ import { DetectionEngineEmptyPage } from './detection_engine_empty_page';
import { DetectionEngineNoIndex } from './detection_engine_no_signal_index';
import { DetectionEngineUserUnauthenticated } from './detection_engine_user_unauthenticated';
import * as i18n from './translations';
import { DetectionEngineTab } from './types';

interface ReduxProps {
filters: esFilters.Filter[];
@@ -51,8 +56,22 @@ export interface DispatchProps {

type DetectionEngineComponentProps = ReduxProps & DispatchProps;

const detectionsTabs = [
{
id: DetectionEngineTab.signals,
name: i18n.SIGNAL,
disabled: false,
},
{
id: DetectionEngineTab.alerts,
name: i18n.ALERT,
disabled: false,
},
];

const DetectionEngineComponent = React.memo<DetectionEngineComponentProps>(
({ filters, query, setAbsoluteRangeDatePicker }) => {
const { tabName = DetectionEngineTab.signals } = useParams();
const {
loading,
isSignalIndexExists,
@@ -87,6 +106,25 @@ const DetectionEngineComponent = React.memo<DetectionEngineComponentProps>(
</WrapperPage>
);
}

const tabs = useMemo(
() => (
<EuiTabs>
{detectionsTabs.map(tab => (
<EuiTab
isSelected={tab.id === tabName}
disabled={tab.disabled}
key={tab.id}
href={`#/${DETECTION_ENGINE_PAGE_NAME}/${tab.id}`}
>
{tab.name}
</EuiTab>
))}
</EuiTabs>
),
[detectionsTabs, tabName]
);

return (
<>
{hasIndexWrite != null && !hasIndexWrite && <NoWriteSignalsCallOut />}
@@ -99,7 +137,6 @@ const DetectionEngineComponent = React.memo<DetectionEngineComponentProps>(
</FiltersGlobal>
<WrapperPage>
<HeaderPage
border
subtitle={
lastSignals != null && (
<>
@@ -117,26 +154,49 @@ const DetectionEngineComponent = React.memo<DetectionEngineComponentProps>(
</HeaderPage>

<GlobalTime>
{({ to, from }) => (
{({ to, from, deleteQuery, setQuery }) => (
<>
<SignalsHistogramPanel
filters={filters}
from={from}
loadingInitial={loading}
query={query}
stackByOptions={signalsHistogramOptions}
to={to}
updateDateRange={updateDateRangeCallback}
/>
{tabs}
<EuiSpacer />
<SignalsTable
loading={loading}
hasIndexWrite={hasIndexWrite ?? false}
canUserCRUD={canUserCRUD ?? false}
from={from}
signalsIndex={signalIndexName ?? ''}
to={to}
/>
{tabName === DetectionEngineTab.signals && (
<>
<SignalsHistogramPanel
filters={filters}
from={from}
loadingInitial={loading}
query={query}
stackByOptions={signalsHistogramOptions}
to={to}
updateDateRange={updateDateRangeCallback}
/>
<EuiSpacer size="l" />
<SignalsTable
loading={loading}
hasIndexWrite={hasIndexWrite ?? false}
canUserCRUD={canUserCRUD ?? false}
from={from}
signalsIndex={signalIndexName ?? ''}
to={to}
/>
</>
)}
{tabName === DetectionEngineTab.alerts && (
<>
<AlertsByCategory
deleteQuery={deleteQuery}
filters={filters}
from={from}
hideHeaderChildren={true}
indexPattern={indexPattern}
query={query}
setAbsoluteRangeDatePicker={setAbsoluteRangeDatePicker!}
setQuery={setQuery}
to={to}
/>
<EuiSpacer size="l" />
<AlertsTable endDate={to} startDate={from} />
</>
)}
</>
)}
</GlobalTime>
16 changes: 12 additions & 4 deletions x-pack/legacy/plugins/siem/public/pages/detection_engine/index.tsx
Original file line number Diff line number Diff line change
@@ -7,12 +7,13 @@
import React from 'react';
import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom';

import { ManageUserInfo } from './components/user_info';
import { CreateRuleComponent } from './rules/create';
import { DetectionEngine } from './detection_engine';
import { EditRuleComponent } from './rules/edit';
import { RuleDetails } from './rules/details';
import { RulesComponent } from './rules';
import { ManageUserInfo } from './components/user_info';
import { DetectionEngineTab } from './types';

const detectionEnginePath = `/:pageName(detection-engine)`;

@@ -21,7 +22,11 @@ type Props = Partial<RouteComponentProps<{}>> & { url: string };
export const DetectionEngineContainer = React.memo<Props>(() => (
<ManageUserInfo>
<Switch>
<Route exact path={detectionEnginePath} strict>
<Route
exact
path={`${detectionEnginePath}/:tabName(${DetectionEngineTab.signals}|${DetectionEngineTab.alerts})`}
strict
>
<DetectionEngine />
</Route>
<Route exact path={`${detectionEnginePath}/rules`}>
@@ -30,7 +35,7 @@ export const DetectionEngineContainer = React.memo<Props>(() => (
<Route exact path={`${detectionEnginePath}/rules/create`}>
<CreateRuleComponent />
</Route>
<Route exact path={`${detectionEnginePath}/rules/id/:ruleId`}>
<Route exact path={`${detectionEnginePath}/rules/id/:ruleId/`}>
<RuleDetails />
</Route>
<Route exact path={`${detectionEnginePath}/rules/id/:ruleId/edit`}>
@@ -39,7 +44,10 @@ export const DetectionEngineContainer = React.memo<Props>(() => (
<Route
path="/detection-engine/"
render={({ location: { search = '' } }) => (
<Redirect from="/detection-engine/" to={`/detection-engine${search}`} />
<Redirect
from="/detection-engine/"
to={`/detection-engine/${DetectionEngineTab.signals}${search}`}
/>
)}
/>
</Switch>
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ import {
EuiSpacer,
EuiHealth,
EuiTab,
EuiTabs,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { memo, useCallback, useMemo, useState } from 'react';
@@ -78,14 +79,19 @@ export interface DispatchProps {
}>;
}

enum RuleDetailTabs {
signals = 'signals',
failures = 'failures',
}

const ruleDetailTabs = [
{
id: 'signal',
id: RuleDetailTabs.signals,
name: detectionI18n.SIGNAL,
disabled: false,
},
{
id: 'failure',
id: RuleDetailTabs.failures,
name: i18n.FAILURE_HISTORY_TAB,
disabled: false,
},
@@ -106,7 +112,7 @@ const RuleDetailsComponent = memo<RuleDetailsComponentProps>(
} = useUserInfo();
const { ruleId } = useParams();
const [isLoading, rule] = useRule(ruleId);
const [ruleDetailTab, setRuleDetailTab] = useState('signal');
const [ruleDetailTab, setRuleDetailTab] = useState(RuleDetailTabs.signals);
const { aboutRuleData, defineRuleData, scheduleRuleData } = getStepsData({
rule,
detailsView: true,
@@ -187,22 +193,27 @@ const RuleDetailsComponent = memo<RuleDetailsComponentProps>(
: 'subdued';

const tabs = useMemo(
() =>
ruleDetailTabs.map(tab => (
<EuiTab
onClick={() => setRuleDetailTab(tab.id)}
isSelected={tab.id === ruleDetailTab}
disabled={tab.disabled}
key={tab.name}
>
{tab.name}
</EuiTab>
)),
() => (
<EuiTabs>
{ruleDetailTabs.map(tab => (
<EuiTab
onClick={() => setRuleDetailTab(tab.id)}
isSelected={tab.id === ruleDetailTab}
disabled={tab.disabled}
key={tab.id}
>
{tab.name}
</EuiTab>
))}
</EuiTabs>
),
[ruleDetailTabs, ruleDetailTab, setRuleDetailTab]
);
const ruleError = useMemo(
() =>
rule?.status === 'failed' && ruleDetailTab === 'signal' && rule?.last_failure_at != null ? (
rule?.status === 'failed' &&
ruleDetailTab === RuleDetailTabs.signals &&
rule?.last_failure_at != null ? (
<RuleStatusFailedCallOut
message={rule?.last_failure_message ?? ''}
date={rule?.last_failure_at}
@@ -316,7 +327,7 @@ const RuleDetailsComponent = memo<RuleDetailsComponentProps>(
{ruleError}
{tabs}
<EuiSpacer />
{ruleDetailTab === 'signal' && (
{ruleDetailTab === RuleDetailTabs.signals && (
<>
<EuiFlexGroup>
<EuiFlexItem component="section" grow={1}>
@@ -381,7 +392,9 @@ const RuleDetailsComponent = memo<RuleDetailsComponentProps>(
)}
</>
)}
{ruleDetailTab === 'failure' && <FailureHistory id={rule?.id} />}
{ruleDetailTab === RuleDetailTabs.failures && (
<FailureHistory id={rule?.id} />
)}
</WrapperPage>
</StickyContainer>
)}
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ export const ADD_NEW_RULE = i18n.translate('xpack.siem.detectionEngine.rules.add
});

export const PAGE_TITLE = i18n.translate('xpack.siem.detectionEngine.rules.pageTitle', {
defaultMessage: 'Rules',
defaultMessage: 'Signal detection rules',
});

export const REFRESH = i18n.translate('xpack.siem.detectionEngine.rules.allRules.refreshTitle', {
Original file line number Diff line number Diff line change
@@ -22,8 +22,12 @@ export const SIGNAL = i18n.translate('xpack.siem.detectionEngine.signalTitle', {
defaultMessage: 'Signals',
});

export const ALERT = i18n.translate('xpack.siem.detectionEngine.alertTitle', {
defaultMessage: 'Third-party alerts',
});

export const BUTTON_MANAGE_RULES = i18n.translate('xpack.siem.detectionEngine.buttonManageRules', {
defaultMessage: 'Manage rules',
defaultMessage: 'Manage signal detection rules',
});

export const PANEL_SUBTITLE_SHOWING = i18n.translate(
10 changes: 10 additions & 0 deletions x-pack/legacy/plugins/siem/public/pages/detection_engine/types.ts
Original file line number Diff line number Diff line change
@@ -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;
* you may not use this file except in compliance with the Elastic License.
*/

export enum DetectionEngineTab {
signals = 'signals',
alerts = 'alerts',
}
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@ const AuthenticationTableManage = manageQuery(AuthenticationTable);
const ID = 'authenticationsOverTimeQuery';
const authStackByOptions: MatrixHistogramOption[] = [
{
text: i18n.NAVIGATION_AUTHENTICATIONS_STACK_BY_EVENT_TYPE,
text: 'event.type',
value: 'event.type',
},
];
@@ -71,7 +71,6 @@ export const AuthenticationsQueryTabBody = ({
isAuthenticationsHistogram={true}
dataKey="AuthenticationsHistogram"
defaultStackByOption={authStackByOptions[0]}
deleteQuery={deleteQuery}
endDate={endDate}
errorMessage={i18n.ERROR_FETCHING_AUTHENTICATIONS_DATA}
filterQuery={filterQuery}
Original file line number Diff line number Diff line change
@@ -20,11 +20,11 @@ const EVENTS_HISTOGRAM_ID = 'eventsOverTimeQuery';

export const eventsStackByOptions: MatrixHistogramOption[] = [
{
text: i18n.NAVIGATION_EVENTS_STACK_BY_EVENT_ACTION,
text: 'event.action',
value: 'event.action',
},
{
text: i18n.NAVIGATION_EVENTS_STACK_BY_EVENT_DATASET,
text: 'event.dataset',
value: 'event.dataset',
},
];
@@ -50,7 +50,6 @@ export const EventsQueryTabBody = ({
<MatrixHistogramContainer
dataKey="EventsHistogram"
defaultStackByOption={eventsStackByOptions[0]}
deleteQuery={deleteQuery}
endDate={endDate}
isEventsHistogram={true}
errorMessage={i18n.ERROR_FETCHING_EVENTS_DATA}
21 changes: 0 additions & 21 deletions x-pack/legacy/plugins/siem/public/pages/hosts/translations.ts
Original file line number Diff line number Diff line change
@@ -28,13 +28,6 @@ export const NAVIGATION_AUTHENTICATIONS_TITLE = i18n.translate(
}
);

export const NAVIGATION_AUTHENTICATIONS_STACK_BY_EVENT_TYPE = i18n.translate(
'xpack.siem.hosts.navigation.authentications.stackByEventType',
{
defaultMessage: 'event type',
}
);

export const NAVIGATION_UNCOMMON_PROCESSES_TITLE = i18n.translate(
'xpack.siem.hosts.navigation.uncommonProcessesTitle',
{
@@ -53,20 +46,6 @@ export const NAVIGATION_EVENTS_TITLE = i18n.translate('xpack.siem.hosts.navigati
defaultMessage: 'Events',
});

export const NAVIGATION_EVENTS_STACK_BY_EVENT_ACTION = i18n.translate(
'xpack.siem.hosts.navigation.eventsStackByEventAction',
{
defaultMessage: 'action',
}
);

export const NAVIGATION_EVENTS_STACK_BY_EVENT_DATASET = i18n.translate(
'xpack.siem.hosts.navigation.eventsStackByEventDataset',
{
defaultMessage: 'dataset',
}
);

export const NAVIGATION_ALERTS_TITLE = i18n.translate('xpack.siem.hosts.navigation.alertsTitle', {
defaultMessage: 'Alerts',
});
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@ const NetworkDnsTableManage = manageQuery(NetworkDnsTable);

const dnsStackByOptions: MatrixHistogramOption[] = [
{
text: i18n.STACK_BY_DOMAIN,
text: 'dns.question.registered_domain',
value: 'dns.question.registered_domain',
},
];
@@ -70,7 +70,6 @@ export const DnsQueryTabBody = ({
title={getTitle}
type={networkModel.NetworkType.page}
updateDateRange={updateDateRange}
showLegend={false}
/>
<EuiSpacer />
<NetworkDnsQuery
Original file line number Diff line number Diff line change
@@ -22,10 +22,6 @@ export const NAVIGATION_DNS_TITLE = i18n.translate('xpack.siem.network.navigatio
defaultMessage: 'DNS',
});

export const STACK_BY_DOMAIN = i18n.translate('xpack.siem.hosts.dns.stackByDomain', {
defaultMessage: 'unique domains',
});

export const ERROR_FETCHING_DNS_DATA = i18n.translate(
'xpack.siem.hosts.navigation.dns.histogram.errorFetchingDnsData',
{
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@

import { EuiButton } from '@elastic/eui';
import numeral from '@elastic/numeral';
import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useEffect, useMemo } from 'react';
import { esFilters, IIndexPattern, Query } from 'src/plugins/data/public';
import styled from 'styled-components';

@@ -16,16 +16,15 @@ import {
UNIT,
} from '../../../components/alerts_viewer/translations';
import { alertsStackByOptions } from '../../../components/alerts_viewer';
import { getTabsOnHostsUrl } from '../../../components/link_to/redirect_to_hosts';
import { getDetectionEngineAlertUrl } from '../../../components/link_to/redirect_to_detection_engine';
import { MatrixHistogramContainer } from '../../../containers/matrix_histogram';
import { MatrixHistogramGqlQuery } from '../../../containers/matrix_histogram/index.gql_query';
import { MatrixHistogramOption } from '../../../components/matrix_histogram/types';
import { useKibana, useUiSetting$ } from '../../../lib/kibana';
import { convertToBuildEsQuery } from '../../../lib/keury';
import { SetAbsoluteRangeDatePicker } from '../../network/types';
import { esQuery } from '../../../../../../../../src/plugins/data/public';
import { inputsModel } from '../../../store';
import { HostsTableType, HostsType } from '../../../store/hosts/model';
import { HostsType } from '../../../store/hosts/model';
import { DEFAULT_NUMBER_FORMAT } from '../../../../common/constants';

import * as i18n from '../translations';
@@ -39,6 +38,7 @@ interface Props {
deleteQuery?: ({ id }: { id: string }) => void;
filters?: esFilters.Filter[];
from: number;
hideHeaderChildren?: boolean;
indexPattern: IIndexPattern;
query?: Query;
setAbsoluteRangeDatePicker: SetAbsoluteRangeDatePicker;
@@ -60,14 +60,24 @@ export const AlertsByCategory = React.memo<Props>(
deleteQuery,
filters = NO_FILTERS,
from,
hideHeaderChildren = false,
indexPattern,
query = DEFAULT_QUERY,
setAbsoluteRangeDatePicker,
setQuery,
to,
}) => {
useEffect(() => {
return () => {
if (deleteQuery) {
deleteQuery({ id: ID });
}
};
}, []);

const kibana = useKibana();
const [defaultNumberFormat] = useUiSetting$<string>(DEFAULT_NUMBER_FORMAT);

const updateDateRangeCallback = useCallback(
(min: number, max: number) => {
setAbsoluteRangeDatePicker!({ id: 'global', from: min, to: max });
@@ -76,17 +86,11 @@ export const AlertsByCategory = React.memo<Props>(
);
const alertsCountViewAlertsButton = useMemo(
() => (
<ViewAlertsButton href={getTabsOnHostsUrl(HostsTableType.alerts)}>
{i18n.VIEW_ALERTS}
</ViewAlertsButton>
<ViewAlertsButton href={getDetectionEngineAlertUrl()}>{i18n.VIEW_ALERTS}</ViewAlertsButton>
),
[]
);

const getTitle = useCallback(
(option: MatrixHistogramOption) => i18n.ALERTS_COUNT_BY(option.text),
[]
);
const getSubtitle = useCallback(
(totalCount: number) =>
`${SHOWING}: ${numeral(totalCount).format(defaultNumberFormat)} ${UNIT(totalCount)}`,
@@ -96,7 +100,6 @@ export const AlertsByCategory = React.memo<Props>(
return (
<MatrixHistogramContainer
dataKey="AlertsHistogram"
deleteQuery={deleteQuery}
defaultStackByOption={alertsStackByOptions[0]}
endDate={to}
errorMessage={ERROR_FETCHING_ALERTS_DATA}
@@ -106,7 +109,7 @@ export const AlertsByCategory = React.memo<Props>(
queries: [query],
filters,
})}
headerChildren={alertsCountViewAlertsButton}
headerChildren={hideHeaderChildren ? null : alertsCountViewAlertsButton}
id={ID}
isAlertsHistogram={true}
legendPosition={'right'}
@@ -115,7 +118,7 @@ export const AlertsByCategory = React.memo<Props>(
sourceId="default"
stackByOptions={alertsStackByOptions}
startDate={from}
title={getTitle}
title={i18n.ALERTS_GRAPH_TITLE}
subtitle={getSubtitle}
type={HostsType.page}
updateDateRange={updateDateRangeCallback}
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@

import { EuiButton } from '@elastic/eui';
import numeral from '@elastic/numeral';
import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useEffect, useMemo } from 'react';
import { esFilters, IIndexPattern, Query } from 'src/plugins/data/public';
import styled from 'styled-components';

@@ -66,8 +66,17 @@ export const EventsByDataset = React.memo<Props>(
setQuery,
to,
}) => {
useEffect(() => {
return () => {
if (deleteQuery) {
deleteQuery({ id: ID });
}
};
}, []);

const kibana = useKibana();
const [defaultNumberFormat] = useUiSetting$<string>(DEFAULT_NUMBER_FORMAT);

const updateDateRangeCallback = useCallback(
(min: number, max: number) => {
setAbsoluteRangeDatePicker!({ id: 'global', from: min, to: max });
@@ -96,7 +105,6 @@ export const EventsByDataset = React.memo<Props>(
return (
<MatrixHistogramContainer
dataKey="EventsHistogram"
deleteQuery={deleteQuery}
defaultStackByOption={eventsStackByOptions[1]}
endDate={to}
errorMessage={ERROR_FETCHING_EVENTS_DATA}
Original file line number Diff line number Diff line change
@@ -12,6 +12,10 @@ export const ALERTS_COUNT_BY = (groupByField: string) =>
defaultMessage: 'Alerts count by {groupByField}',
});

export const ALERTS_GRAPH_TITLE = i18n.translate('xpack.siem.overview.alertsGraphTitle', {
defaultMessage: 'Alert detection frequency',
});

export const EVENTS_COUNT_BY = (groupByField: string) =>
i18n.translate('xpack.siem.overview.eventsCountByTitle', {
values: { groupByField },