Skip to content
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
06ec35b
wip - basics working with inspect
yctercero Sep 30, 2020
9039fab
simplified code
yctercero Sep 30, 2020
0a0cf94
wip - got threshold preview working
yctercero Sep 30, 2020
7bceb49
removed preview for ml
yctercero Sep 30, 2020
237bade
adjusted css
yctercero Oct 1, 2020
ff7f8c5
wip - adding unit tests
yctercero Oct 4, 2020
cc01e09
addes some more unit tests, added some more logic around errors and w…
yctercero Oct 5, 2020
48fd4cf
remove unnecessary code, and duplicate translations
yctercero Oct 5, 2020
d7f6eb1
cleanup
yctercero Oct 5, 2020
a25575e
remove preview from threat match
yctercero Oct 5, 2020
e07ef98
hide histogram on timeframe selection change
yctercero Oct 5, 2020
2374e11
cleanup
yctercero Oct 5, 2020
ca6977e
updated types - thank you Ryland!
yctercero Oct 5, 2020
3036723
fix up types
yctercero Oct 5, 2020
521497f
fix threshold preview bug
yctercero Oct 5, 2020
b034ebc
removes missing param from threshold query if threshold field specifi…
yctercero Oct 5, 2020
b283bd9
Updated threshold rule graph when threshold field is defined per feed…
yctercero Oct 6, 2020
edb7c3f
Fixed getMatrixHistogram spelling error
yctercero Oct 6, 2020
6b0be88
use existing sequence check util
yctercero Oct 6, 2020
8190b0f
disable preview button if errors exist, note this wont work for eql q…
yctercero Oct 6, 2020
ece38ef
fixed circular dependency issue
yctercero Oct 6, 2020
b04ec68
refactored code to use existing matrix histogram hook and component f…
yctercero Oct 6, 2020
87721a0
fix typecheck
yctercero Oct 6, 2020
9a42861
formatted graphs to look alike
yctercero Oct 6, 2020
84b7fef
undid some of the type moves into common that werent needed, cleaning…
yctercero Oct 6, 2020
aa4af5f
added buckets info to existing return objec per feedback
yctercero Oct 6, 2020
a750613
changed behavior to display graph when 0 hits so they can still inspe…
yctercero Oct 6, 2020
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
45 changes: 45 additions & 0 deletions x-pack/plugins/security_solution/common/detection_engine/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,48 @@ import { AlertAction } from '../../../alerts/common';
export type RuleAlertAction = Omit<AlertAction, 'actionTypeId'> & {
action_type_id: string;
};

export type SearchTypes =
| string
| string[]
| number
| number[]
| boolean
| boolean[]
| object
| object[]
| undefined;

export interface Explanation {
value: number;
description: string;
details: Explanation[];
}

export interface TotalValue {
value: number;
relation: string;
}

export interface BaseHit<T> {
_index: string;
_id: string;
_source: T;
}

export interface EqlSequence<T> {
join_keys: SearchTypes[];
events: Array<BaseHit<T>>;
}

export interface EqlSearchResponse<T> {
is_partial: boolean;
is_running: boolean;
took: number;
timed_out: boolean;
hits: {
total: TotalValue;
sequences?: Array<EqlSequence<T>>;
events?: Array<BaseHit<T>>;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export interface MatrixHistogramRequestOptions extends RequestBasicOptions {
timerange: TimerangeInput;
histogramType: MatrixHistogramType;
stackByField: string;
threshold?: { field: string | undefined; value: number } | undefined;
inspect?: Maybe<Inspect>;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,13 @@ const checkIfAnyValidSeriesExist = (
export const BarChartBaseComponent = ({
data,
forceHiddenLegend = false,
yAxisTitle,
...chartConfigs
}: {
data: ChartSeriesData[];
width: string | null | undefined;
height: string | null | undefined;
yAxisTitle?: string | undefined;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not affect any component as it is optional. When undefined, no y axis title is shown.

configs?: ChartSeriesConfigs | undefined;
forceHiddenLegend?: boolean;
}) => {
Expand Down Expand Up @@ -115,6 +117,7 @@ export const BarChartBaseComponent = ({
},
}}
tickFormat={yTickFormatter}
title={yAxisTitle}
/>
</Chart>
) : null;
Expand Down Expand Up @@ -158,6 +161,7 @@ export const BarChartComponent: React.FC<BarChartComponentProps> = ({
[barChart, stackByField, timelineId]
);

const yAxisTitle = get('yAxisTitle', configs);
const customHeight = get('customHeight', configs);
const customWidth = get('customWidth', configs);
const chartHeight = getChartHeight(customHeight, height);
Expand All @@ -170,6 +174,7 @@ export const BarChartComponent: React.FC<BarChartComponentProps> = ({
<BarChartBase
configs={configs}
data={barChart}
yAxisTitle={yAxisTitle}
forceHiddenLegend={stackByField != null}
height={chartHeight}
width={chartHeight}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,14 @@ export interface ChartSeriesConfigs {
series?: {
xScaleType?: ScaleType | undefined;
yScaleType?: ScaleType | undefined;
stackAccessors?: string[] | undefined;
};
axis?: {
xTickFormatter?: TickFormatter | undefined;
yTickFormatter?: TickFormatter | undefined;
tickSize?: number | undefined;
};
yAxisTitle?: string | undefined;
settings?: Partial<SettingsSpecProps>;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export type MatrixHistogramComponentProps = MatrixHistogramProps &
defaultStackByOption: MatrixHistogramOption;
errorMessage: string;
headerChildren?: React.ReactNode;
footerChildren?: React.ReactNode;
hideHistogramIfEmpty?: boolean;
histogramType: MatrixHistogramType;
id: string;
Expand All @@ -47,6 +48,7 @@ export type MatrixHistogramComponentProps = MatrixHistogramProps &
subtitle?: string | GetSubTitle;
timelineId?: string;
title: string | GetTitle;
yTitle?: string | undefined;
};

const DEFAULT_PANEL_HEIGHT = 300;
Expand All @@ -68,6 +70,7 @@ export const MatrixHistogramComponent: React.FC<MatrixHistogramComponentProps> =
errorMessage,
filterQuery,
headerChildren,
footerChildren,
histogramType,
hideHistogramIfEmpty = false,
id,
Expand All @@ -86,6 +89,7 @@ export const MatrixHistogramComponent: React.FC<MatrixHistogramComponentProps> =
title,
titleSize,
yTickFormatter,
yTitle,
}) => {
const dispatch = useDispatch();
const handleBrushEnd = useCallback(
Expand Down Expand Up @@ -114,8 +118,18 @@ export const MatrixHistogramComponent: React.FC<MatrixHistogramComponentProps> =
onBrushEnd: handleBrushEnd,
yTickFormatter,
showLegend,
yTitle,
}),
[chartHeight, startDate, legendPosition, endDate, handleBrushEnd, yTickFormatter, showLegend]
[
chartHeight,
startDate,
legendPosition,
endDate,
handleBrushEnd,
yTickFormatter,
showLegend,
yTitle,
]
);
const [isInitialLoading, setIsInitialLoading] = useState(true);
const [selectedStackByOption, setSelectedStackByOption] = useState<MatrixHistogramOption>(
Expand Down Expand Up @@ -229,6 +243,11 @@ export const MatrixHistogramComponent: React.FC<MatrixHistogramComponentProps> =
timelineId={timelineId}
/>
)}
{footerChildren != null && (
<EuiFlexGroup gutterSize="none" direction="row">
{footerChildren}
</EuiFlexGroup>
)}
</HistogramPanel>
</InspectButtonContainer>
{showSpacer && <EuiSpacer data-test-subj="spacer" size="l" />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export interface MatrixHistogramQueryProps {
stackByField: string;
startDate: string;
histogramType: MatrixHistogramType;
threshold?: { field: string | undefined; value: number } | undefined;
}

export interface MatrixHistogramProps extends MatrixHistogramBasicProps {
Expand Down Expand Up @@ -104,6 +105,7 @@ export interface BarchartConfigs {
yTickFormatter: TickFormatter;
tickSize: number;
};
yAxisTitle: string | undefined;
settings: {
legendPosition: Position;
onBrushEnd: UpdateDateRange;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ interface GetBarchartConfigsProps {
onBrushEnd: UpdateDateRange;
yTickFormatter?: (value: number) => string;
showLegend?: boolean;
yTitle?: string | undefined;
}

export const DEFAULT_CHART_HEIGHT = 174;
Expand All @@ -32,6 +33,7 @@ export const getBarchartConfigs = ({
onBrushEnd,
yTickFormatter,
showLegend,
yTitle,
}: GetBarchartConfigsProps): BarchartConfigs => ({
series: {
xScaleType: ScaleType.Time,
Expand All @@ -43,6 +45,7 @@ export const getBarchartConfigs = ({
yTickFormatter: yTickFormatter != null ? yTickFormatter : DEFAULT_Y_TICK_FORMATTER,
tickSize: 8,
},
yAxisTitle: yTitle,
settings: {
legendPosition: legendPosition ?? Position.Right,
onBrushEnd,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import deepEqual from 'fast-deep-equal';
import { noop } from 'lodash/fp';
import { getOr, noop } from 'lodash/fp';
import { useCallback, useEffect, useRef, useState } from 'react';

import { MatrixHistogramQueryProps } from '../../components/matrix_histogram/types';
Expand Down Expand Up @@ -44,7 +44,15 @@ export const useMatrixHistogram = ({
indexNames,
stackByField,
startDate,
}: MatrixHistogramQueryProps): [boolean, UseMatrixHistogramArgs] => {
threshold,
}: MatrixHistogramQueryProps): [
boolean,
UseMatrixHistogramArgs,
Array<{
key: string;
doc_count: number;
}>
] => {
const { data, notifications } = useKibana().services;
const refetch = useRef<inputsModel.Refetch>(noop);
const abortCtrl = useRef(new AbortController());
Expand All @@ -63,6 +71,7 @@ export const useMatrixHistogram = ({
to: endDate,
},
stackByField,
threshold,
});

const [matrixHistogramResponse, setMatrixHistogramResponse] = useState<UseMatrixHistogramArgs>({
Expand All @@ -74,6 +83,12 @@ export const useMatrixHistogram = ({
refetch: refetch.current,
totalCount: -1,
});
const [matrixHistogramBuckets, setMatrixHistogramBuckets] = useState<
Array<{
key: string;
doc_count: number;
}>
>([]);

const hostsSearch = useCallback(
(request: MatrixHistogramRequestOptions) => {
Expand All @@ -91,6 +106,10 @@ export const useMatrixHistogram = ({
next: (response) => {
if (isCompleteResponse(response)) {
if (!didCancel) {
const histogramBuckets: Array<{
key: string;
doc_count: number;
}> = getOr([], 'rawResponse.aggregations.eventActionGroup.buckets', response);
setLoading(false);
setMatrixHistogramResponse((prevResponse) => ({
...prevResponse,
Expand All @@ -99,6 +118,7 @@ export const useMatrixHistogram = ({
refetch: refetch.current,
totalCount: response.totalCount,
}));
setMatrixHistogramBuckets(histogramBuckets);
}
searchSubscription$.unsubscribe();
} else if (isErrorResponse(response)) {
Expand Down Expand Up @@ -144,17 +164,18 @@ export const useMatrixHistogram = ({
to: endDate,
},
stackByField,
threshold,
};
if (!deepEqual(prevRequest, myRequest)) {
return myRequest;
}
return prevRequest;
});
}, [indexNames, endDate, filterQuery, startDate, stackByField, histogramType]);
}, [indexNames, endDate, filterQuery, startDate, stackByField, histogramType, threshold]);

useEffect(() => {
hostsSearch(matrixHistogramRequest);
}, [matrixHistogramRequest, hostsSearch]);

return [loading, matrixHistogramResponse];
return [loading, matrixHistogramResponse, matrixHistogramBuckets];
};
71 changes: 71 additions & 0 deletions x-pack/plugins/security_solution/public/common/hooks/eql/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,21 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { Unit } from '@elastic/datemath';

import { HttpStart } from '../../../../../../../src/core/public';
import { DETECTION_ENGINE_EQL_VALIDATION_URL } from '../../../../common/constants';
import { EqlValidationSchema as EqlValidationRequest } from '../../../../common/detection_engine/schemas/request/eql_validation_schema';
import { EqlValidationSchema as EqlValidationResponse } from '../../../../common/detection_engine/schemas/response/eql_validation_schema';
import { DataPublicPluginStart } from '../../../../../../../src/plugins/data/public';
import {
EqlSearchStrategyRequest,
EqlSearchStrategyResponse,
} from '../../../../../data_enhanced/common';
import { getEqlAggsData, getSequenceAggs } from './helpers';
import { EqlPreviewResponse, Source } from './types';
import { hasEqlSequenceQuery } from '../../../../common/detection_engine/utils';
import { EqlSearchResponse } from '../../../../common/detection_engine/types';

interface ApiParams {
http: HttpStart;
Expand All @@ -29,3 +39,64 @@ export const validateEql = async ({
signal,
});
};

interface AggsParams extends EqlValidationRequest {
data: DataPublicPluginStart;
interval: Unit;
fromTime: string;
toTime: string;
signal: AbortSignal;
}

export const getEqlPreview = async ({
data,
index,
interval,
query,
fromTime,
toTime,
signal,
}: AggsParams): Promise<EqlPreviewResponse> => {
try {
const response = await data.search
.search<EqlSearchStrategyRequest, EqlSearchStrategyResponse<EqlSearchResponse<Source>>>(
{
params: {
// @ts-expect-error allow_no_indices is missing on EqlSearch
allow_no_indices: true,
index: index.join(),
body: {
filter: {
range: {
'@timestamp': {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be using the timestamp override from the rule if available?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first I thought, yes, but we got asked to move the timestamp override field to step 2 and not take it into account. But maybe we want to reconsider? @marrasherrier @dontcallmesherryli

gte: toTime,
lte: fromTime,
format: 'strict_date_optional_time',
},
},
},
query,
// EQL requires a cap, otherwise it defaults to 10
// It also sorts on ascending order, capping it at
// something smaller like 20, made it so that some of
// the more recent events weren't returned
size: 100,
},
},
},
{
strategy: 'eql',
abortSignal: signal,
}
)
.toPromise();

if (hasEqlSequenceQuery(query)) {
return getSequenceAggs(response, interval, toTime, fromTime);
} else {
return getEqlAggsData(response, interval, toTime, fromTime);
}
} catch (err) {
throw new Error(JSON.stringify(err));
}
};
Loading