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

[SIEM] Detection Engine Create Rule Design Review #1 #54442

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

import {
EuiBadge,
EuiHealth,
EuiIconTip,
EuiLink,
EuiTextColor,
Expand All @@ -17,7 +16,6 @@ import {
} from '@elastic/eui';
import * as H from 'history';
import React from 'react';
import euiLightVars from '@elastic/eui/dist/eui_theme_light.json';
import { getEmptyTagValue } from '../../../../components/empty_value';
import {
deleteRulesAction,
Expand All @@ -32,6 +30,7 @@ import { TableData } from '../types';
import * as i18n from '../translations';
import { PreferenceFormattedDate } from '../../../../components/formatted_date';
import { RuleSwitch } from '../components/rule_switch';
import { SeverityBadge } from '../components/severity_badge';

const getActions = (dispatch: React.Dispatch<Action>, history: H.History) => [
{
Expand Down Expand Up @@ -92,21 +91,7 @@ export const getColumns = (
{
field: 'severity',
name: i18n.COLUMN_SEVERITY,
render: (value: TableData['severity']) => (
<EuiHealth
color={
value === 'low'
? euiLightVars.euiColorVis0
: value === 'medium'
? euiLightVars.euiColorVis5
: value === 'high'
? euiLightVars.euiColorVis7
: euiLightVars.euiColorVis9
}
>
{value}
</EuiHealth>
),
render: (value: TableData['severity']) => <SeverityBadge value={value} />,
truncateText: true,
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,25 @@ const MyEuiFormRow = styled(EuiFormRow)`
}
`;

export const MyAddItemButton = styled(EuiButtonEmpty)`
margin-top: 4px;
&.euiButtonEmpty--xSmall {
font-size: 12px;
}
.euiIcon {
width: 12px;
height: 12px;
}
`;

MyAddItemButton.defaultProps = {
flush: 'left',
iconType: 'plusInCircle',
size: 'xs',
};

export const AddItem = ({
addText,
dataTestSubj,
Expand Down Expand Up @@ -160,9 +179,9 @@ export const AddItem = ({
);
})}

<EuiButtonEmpty size="xs" onClick={addItem} isDisabled={isDisabled} iconType="plusInCircle">
<MyAddItemButton onClick={addItem} isDisabled={isDisabled}>
{addText}
</EuiButtonEmpty>
</MyAddItemButton>
</>
</MyEuiFormRow>
);
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,10 @@ import {
EuiLoadingSpinner,
EuiFlexGroup,
EuiFlexItem,
EuiHealth,
EuiLink,
EuiText,
EuiListGroup,
EuiButtonEmpty,
EuiSpacer,
} from '@elastic/eui';
import euiLightVars from '@elastic/eui/dist/eui_theme_light.json';

import { isEmpty } from 'lodash/fp';
import React from 'react';
Expand All @@ -27,6 +25,11 @@ import { tacticsOptions, techniquesOptions } from '../../../mitre/mitre_tactics_
import { FilterLabel } from './filter_label';
import * as i18n from './translations';
import { BuildQueryBarDescription, BuildThreatsDescription, ListItems } from './types';
import { SeverityBadge } from '../severity_badge';
import ListTreeIcon from './assets/list_tree_icon.svg';

const isNotEmptyArray = (values: string[]) =>
!isEmpty(values) && values.filter(val => !isEmpty(val)).length > 0;

const EuiBadgeWrap = styled(EuiBadge)`
.euiBadge__text {
Expand Down Expand Up @@ -97,10 +100,17 @@ const ThreatsEuiFlexGroup = styled(EuiFlexGroup)`
}
`;

const MyEuiListGroup = styled(EuiListGroup)`
padding: 0px;
.euiListGroupItem__button {
padding: 0px;
const TechniqueLinkItem = styled(EuiButtonEmpty)`
.euiIcon {
width: 8px;
height: 8px;
}
`;

const ReferenceLinkItem = styled(EuiButtonEmpty)`
.euiIcon {
width: 12px;
height: 12px;
}
`;

Expand All @@ -118,28 +128,31 @@ export const buildThreatsDescription = ({
const tactic = tacticsOptions.find(t => t.name === threat.tactic.name);
return (
<EuiFlexItem key={`${threat.tactic.name}-${index}`}>
<EuiText grow={false} size="s">
<h5>
<EuiLink href={threat.tactic.reference} target="_blank">
{tactic != null ? tactic.text : ''}
</EuiLink>
</h5>
<MyEuiListGroup
flush={false}
bordered={false}
listItems={threat.techniques.map(technique => {
const myTechnique = techniquesOptions.find(t => t.name === technique.name);
return {
label: myTechnique != null ? myTechnique.label : '',
href: technique.reference,
target: '_blank',
};
})}
/>
</EuiText>
<EuiLink href={threat.tactic.reference} target="_blank">
{tactic != null ? tactic.text : ''}
</EuiLink>
<EuiFlexGroup gutterSize="none" alignItems="flexStart" direction="column">
{threat.techniques.map(technique => {
const myTechnique = techniquesOptions.find(t => t.name === technique.name);
return (
<EuiFlexItem>
<TechniqueLinkItem
href={technique.reference}
target="_blank"
iconType={ListTreeIcon}
size="xs"
flush="left"
>
{myTechnique != null ? myTechnique.label : ''}
</TechniqueLinkItem>
</EuiFlexItem>
);
})}
</EuiFlexGroup>
</EuiFlexItem>
);
})}
<EuiSpacer />
</ThreatsEuiFlexGroup>
),
},
Expand All @@ -148,12 +161,34 @@ export const buildThreatsDescription = ({
return [];
};

export const buildUnorderedListArrayDescription = (
label: string,
field: string,
values: string[]
): ListItems[] => {
if (isNotEmptyArray(values)) {
return [
{
title: label,
description: (
<ul>
{values.map((val: string) =>
isEmpty(val) ? null : <li key={`${field}-${val}`}>{val}</li>
)}
</ul>
),
},
];
}
return [];
};

export const buildStringArrayDescription = (
label: string,
field: string,
values: string[]
): ListItems[] => {
if (!isEmpty(values) && values.filter(val => !isEmpty(val)).length > 0) {
if (isNotEmptyArray(values)) {
return [
{
title: label,
Expand All @@ -174,46 +209,34 @@ export const buildStringArrayDescription = (
return [];
};

export const buildSeverityDescription = (label: string, value: string): ListItems[] => {
return [
{
title: label,
description: (
<EuiHealth
color={
value === 'low'
? euiLightVars.euiColorVis0
: value === 'medium'
? euiLightVars.euiColorVis5
: value === 'high'
? euiLightVars.euiColorVis7
: euiLightVars.euiColorVis9
}
>
{value}
</EuiHealth>
),
},
];
};
export const buildSeverityDescription = (label: string, value: string): ListItems[] => [
{
title: label,
description: <SeverityBadge value={value} />,
},
];

export const buildUrlsDescription = (label: string, values: string[]): ListItems[] => {
if (!isEmpty(values) && values.filter(val => !isEmpty(val)).length > 0) {
if (isNotEmptyArray(values)) {
return [
{
title: label,
description: (
<EuiListGroup
flush={true}
bordered={false}
listItems={values.map((val: string) => ({
label: val,
href: val,
iconType: 'link',
size: 'xs',
target: '_blank',
}))}
/>
<EuiFlexGroup gutterSize="none" alignItems="flexStart" direction="column">
{values.map((val: string) => (
<EuiFlexItem>
<ReferenceLinkItem
href={val}
target="_blank"
iconType="link"
size="xs"
flush="left"
>
{val}
</ReferenceLinkItem>
</EuiFlexItem>
))}
</EuiFlexGroup>
),
},
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { EuiDescriptionList, EuiFlexGroup, EuiFlexItem, EuiTextArea } from '@elastic/eui';
import { EuiDescriptionList, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { isEmpty, chunk, get, pick } from 'lodash/fp';
import React, { memo, useState } from 'react';
import styled from 'styled-components';

import {
IIndexPattern,
Expand All @@ -26,6 +25,7 @@ import {
buildSeverityDescription,
buildStringArrayDescription,
buildThreatsDescription,
buildUnorderedListArrayDescription,
buildUrlsDescription,
} from './helpers';

Expand All @@ -36,15 +36,6 @@ interface StepRuleDescriptionProps {
schema: FormSchema;
}

const EuiFlexItemWidth = styled(EuiFlexItem)<{ direction: string }>`
${props => (props.direction === 'row' ? 'width : 50%;' : 'width: 100%;')};
`;

const MyEuiTextArea = styled(EuiTextArea)`
max-width: 100%;
height: 80px;
`;

const StepRuleDescriptionComponent: React.FC<StepRuleDescriptionProps> = ({
data,
direction = 'row',
Expand All @@ -62,13 +53,24 @@ const StepRuleDescriptionComponent: React.FC<StepRuleDescriptionProps> = ({
],
[]
);

if (direction === 'row') {
return (
<EuiFlexGroup>
{chunk(Math.ceil(listItems.length / 2), listItems).map((chunkListItems, index) => (
<EuiFlexItem key={`description-step-rule-${index}`}>
<EuiDescriptionList listItems={chunkListItems} />
</EuiFlexItem>
))}
</EuiFlexGroup>
);
}

return (
<EuiFlexGroup gutterSize="none" direction={direction} justifyContent="spaceAround">
{chunk(Math.ceil(listItems.length / 2), listItems).map((chunkListItems, index) => (
<EuiFlexItemWidth direction={direction} key={`description-step-rule-${index}`} grow={false}>
<EuiDescriptionList listItems={chunkListItems} />
</EuiFlexItemWidth>
))}
<EuiFlexGroup>
<EuiFlexItem key={`description-step-rule`}>
<EuiDescriptionList listItems={listItems} />
</EuiFlexItem>
</EuiFlexGroup>
);
};
Expand Down Expand Up @@ -123,18 +125,28 @@ const getDescriptionItem = (
return [
{
title: label,
description: <MyEuiTextArea value={get(field, value)} readOnly={true} />,
description: get(field, value),
},
];
} else if (field === 'references') {
const urls: string[] = get(field, value);
return buildUrlsDescription(label, urls);
} else if (field === 'falsePositives') {
const values: string[] = get(field, value);
return buildUnorderedListArrayDescription(label, field, values);
} else if (Array.isArray(get(field, value))) {
const values: string[] = get(field, value);
return buildStringArrayDescription(label, field, values);
} else if (field === 'severity') {
const val: string = get(field, value);
return buildSeverityDescription(label, val);
} else if (field === 'riskScore') {
return [
{
title: label,
description: get(field, value),
},
];
} else if (field === 'timeline') {
const timeline = get(field, value) as FieldValueTimeline;
return [
Expand Down
Loading