Skip to content

Commit

Permalink
Adds rule execution logic for timestampOverride, integrates autocompl…
Browse files Browse the repository at this point in the history
…ete for severityOverride, and fixes rule details description rollup
  • Loading branch information
spong committed Jul 14, 2020
1 parent c66ae07 commit fc4c0e1
Show file tree
Hide file tree
Showing 17 changed files with 218 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ interface AutocompleteFieldMatchProps {
isLoading: boolean;
isDisabled: boolean;
isClearable: boolean;
fieldInputWidth?: number;
onChange: (arg: string) => void;
}

Expand All @@ -33,6 +34,7 @@ export const AutocompleteFieldMatchComponent: React.FC<AutocompleteFieldMatchPro
isLoading,
isDisabled = false,
isClearable = false,
fieldInputWidth,
onChange,
}): JSX.Element => {
const [isLoadingSuggestions, suggestions, updateSuggestions] = useFieldValueAutocomplete({
Expand Down Expand Up @@ -97,6 +99,7 @@ export const AutocompleteFieldMatchComponent: React.FC<AutocompleteFieldMatchPro
isInvalid={!isValid}
sortMatchesBy="startsWith"
data-test-subj="valuesAutocompleteComboBox matchComboxBox"
style={fieldInputWidth ? { width: `${fieldInputWidth}px` } : {}}
fullWidth
async
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,21 @@ export const ALERTS_DOCUMENT_TYPE = i18n.translate(
export const OPEN_ALERTS = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.openAlertsTitle',
{
defaultMessage: 'Open alerts',
defaultMessage: 'Open',
}
);

export const CLOSED_ALERTS = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.closedAlertsTitle',
{
defaultMessage: 'Closed alerts',
defaultMessage: 'Closed',
}
);

export const IN_PROGRESS_ALERTS = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.inProgressAlertsTitle',
{
defaultMessage: 'In progress alerts',
defaultMessage: 'In progress',
}
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import React from 'react';
import { shallow } from 'enzyme';
import { mount, shallow } from 'enzyme';
import { EuiLoadingSpinner } from '@elastic/eui';

import { coreMock } from '../../../../../../../../src/core/public/mocks';
Expand Down Expand Up @@ -328,10 +328,19 @@ describe('helpers', () => {

describe('buildSeverityDescription', () => {
test('returns ListItem with passed in label and SeverityBadge component', () => {
const result: ListItems[] = buildSeverityDescription('Test label', 'Test description value');
const result: ListItems[] = buildSeverityDescription({
value: 'low',
mapping: [{ field: 'host.name', operator: 'equals', value: 'hello', severity: 'high' }],
});

expect(result[0].title).toEqual('Test label');
expect(result[0].description).toEqual(<SeverityBadge value="Test description value" />);
expect(result[0].title).toEqual('Severity');
expect(result[0].description).toEqual(<SeverityBadge value="low" />);
expect(result[1].title).toEqual('Severity override');

const wrapper = mount<React.ReactElement>(result[1].description as React.ReactElement);
expect(wrapper.find('[data-test-subj="severityOverrideSeverity0"]').first().text()).toEqual(
'High'
);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@ import {
EuiSpacer,
EuiLink,
EuiText,
EuiIcon,
EuiToolTip,
} from '@elastic/eui';

import { isEmpty } from 'lodash/fp';
import React from 'react';
import styled from 'styled-components';

import * as i18nSeverity from '../severity_mapping/translations';
import * as i18nRiskScore from '../risk_score_mapping/translations';
import { RuleType } from '../../../../../common/detection_engine/types';
import { esFilters } from '../../../../../../../../src/plugins/data/public';

Expand All @@ -29,6 +33,7 @@ import { BuildQueryBarDescription, BuildThreatDescription, ListItems } from './t
import { SeverityBadge } from '../severity_badge';
import ListTreeIcon from './assets/list_tree_icon.svg';
import { assertUnreachable } from '../../../../common/lib/helpers';
import { AboutStepRiskScore, AboutStepSeverity } from '../../../pages/detection_engine/rules/types';

const NoteDescriptionContainer = styled(EuiFlexItem)`
height: 105px;
Expand Down Expand Up @@ -218,11 +223,75 @@ export const buildStringArrayDescription = (
return [];
};

export const buildSeverityDescription = (label: string, value: string): ListItems[] => [
const OverrideColumn = styled(EuiFlexItem)`
width: 125px;
max-width: 125px;
overflow: hidden;
text-overflow: ellipsis;
`;

export const buildSeverityDescription = (severity: AboutStepSeverity): ListItems[] => [
{
title: i18nSeverity.DEFAULT_SEVERITY,
description: <SeverityBadge value={severity.value} />,
},
...severity.mapping.map((severityItem, index) => {
return {
title: index === 0 ? i18nSeverity.SEVERITY_MAPPING : '',
description: (
<EuiFlexGroup alignItems="center">
<OverrideColumn>
<EuiToolTip
content={severityItem.field}
data-test-subj={`severityOverrideField${index}`}
>
<>{severityItem.field}</>
</EuiToolTip>
</OverrideColumn>
<EuiToolTip content={severityItem.value} data-test-subj={`severityOverrideValue${index}`}>
<>{severityItem.value}</>
</EuiToolTip>
<EuiFlexItem grow={false}>
<EuiIcon type={'sortRight'} />
</EuiFlexItem>
<EuiFlexItem>
<SeverityBadge
data-test-subj={`severityOverrideSeverity${index}`}
value={severityItem.severity}
/>
</EuiFlexItem>
</EuiFlexGroup>
),
};
}),
];

export const buildRiskScoreDescription = (riskScore: AboutStepRiskScore): ListItems[] => [
{
title: label,
description: <SeverityBadge value={value} />,
title: i18nRiskScore.RISK_SCORE,
description: riskScore.value,
},
...riskScore.mapping.map((riskScoreItem, index) => {
return {
title: index === 0 ? i18nRiskScore.RISK_SCORE_MAPPING : '',
description: (
<EuiFlexGroup alignItems="center">
<EuiFlexItem>
<EuiToolTip
content={riskScoreItem.field}
data-test-subj={`riskScoreOverrideField${index}`}
>
<>{riskScoreItem.field}</>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiIcon type={'sortRight'} />
</EuiFlexItem>
<EuiFlexItem>{'signal.rule.risk_score'}</EuiFlexItem>
</EuiFlexGroup>
),
};
}),
];

const MyRefUrlLink = styled(EuiLink)`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ describe('description_step', () => {
mockFilterManager
);

expect(result[0].title).toEqual('Severity label');
expect(result[0].title).toEqual('Severity');
expect(React.isValidElement(result[0].description)).toBeTruthy();
});
});
Expand All @@ -418,7 +418,7 @@ describe('description_step', () => {
mockFilterManager
);

expect(result[0].title).toEqual('Risk score label');
expect(result[0].title).toEqual('Risk score');
expect(result[0].description).toEqual(21);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
buildUrlsDescription,
buildNoteDescription,
buildRuleTypeDescription,
buildRiskScoreDescription,
} from './helpers';
import { useSiemJobs } from '../../../../common/components/ml_popover/hooks/use_siem_jobs';
import { buildMlJobDescription } from './ml_job_description';
Expand Down Expand Up @@ -188,18 +189,12 @@ export const getDescriptionItem = (
} else if (Array.isArray(get(field, data))) {
const values: string[] = get(field, data);
return buildStringArrayDescription(label, field, values);
// TODO: Add custom UI for Risk/Severity Mappings (and fix missing label)
} else if (field === 'riskScore') {
const val: AboutStepRiskScore = get(field, data);
return [
{
title: label,
description: val.value,
},
];
const values: AboutStepRiskScore = get(field, data);
return buildRiskScoreDescription(values);
} else if (field === 'severity') {
const val: AboutStepSeverity = get(field, data);
return buildSeverityDescription(label, val.value);
const values: AboutStepSeverity = get(field, data);
return buildSeverityDescription(values);
} else if (field === 'timeline') {
const timeline = get(field, data) as FieldValueTimeline;
return [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,25 +51,25 @@ export const RiskScoreField = ({
indices,
placeholder,
}: RiskScoreFieldProps) => {
const [isRiskScoreMappingSelected, setIsRiskScoreMappingSelected] = useState(false);
const [isRiskScoreMappingChecked, setIsRiskScoreMappingChecked] = useState(false);
const [initialFieldCheck, setInitialFieldCheck] = useState(true);

const fieldTypeFilter = useMemo(() => ['number'], []);

useEffect(() => {
if (
!isRiskScoreMappingSelected &&
!isRiskScoreMappingChecked &&
initialFieldCheck &&
(field.value as AboutStepRiskScore).mapping?.length > 0
) {
setIsRiskScoreMappingSelected(true);
setIsRiskScoreMappingChecked(true);
setInitialFieldCheck(false);
}
}, [
field,
initialFieldCheck,
isRiskScoreMappingSelected,
setIsRiskScoreMappingSelected,
isRiskScoreMappingChecked,
setIsRiskScoreMappingChecked,
setInitialFieldCheck,
]);

Expand All @@ -90,10 +90,6 @@ export const RiskScoreField = ({
[field]
);

const handleRiskScoreMappingSelected = useCallback(() => {
setIsRiskScoreMappingSelected(!isRiskScoreMappingSelected);
}, [isRiskScoreMappingSelected, setIsRiskScoreMappingSelected]);

const selectedField = useMemo(() => {
const existingField = (field.value as AboutStepRiskScore).mapping?.[0]?.field ?? '';
const [newSelectedField] = indices.fields.filter(
Expand All @@ -102,11 +98,15 @@ export const RiskScoreField = ({
return newSelectedField;
}, [field.value, indices]);

const handleRiskScoreMappingChecked = useCallback(() => {
setIsRiskScoreMappingChecked(!isRiskScoreMappingChecked);
}, [isRiskScoreMappingChecked, setIsRiskScoreMappingChecked]);

const riskScoreLabel = useMemo(() => {
return (
<div>
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>{i18n.RISK_SCORE}</EuiFlexItem>
<EuiFlexItem>{i18n.DEFAULT_RISK_SCORE}</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="xs" />
<EuiText size={'xs'}>{i18n.RISK_SCORE_DESCRIPTION}</EuiText>
Expand All @@ -117,12 +117,12 @@ export const RiskScoreField = ({
const riskScoreMappingLabel = useMemo(() => {
return (
<div>
<EuiFlexGroup alignItems="center" gutterSize="s" onClick={handleRiskScoreMappingSelected}>
<EuiFlexGroup alignItems="center" gutterSize="s" onClick={handleRiskScoreMappingChecked}>
<EuiFlexItem grow={false}>
<EuiCheckbox
id={`risk_score-mapping-override`}
checked={isRiskScoreMappingSelected}
onChange={handleRiskScoreMappingSelected}
checked={isRiskScoreMappingChecked}
onChange={handleRiskScoreMappingChecked}
/>
</EuiFlexItem>
<EuiFlexItem>{i18n.RISK_SCORE_MAPPING}</EuiFlexItem>
Expand All @@ -133,7 +133,7 @@ export const RiskScoreField = ({
</NestedContent>
</div>
);
}, [handleRiskScoreMappingSelected, isRiskScoreMappingSelected]);
}, [handleRiskScoreMappingChecked, isRiskScoreMappingChecked]);

return (
<EuiFlexGroup>
Expand Down Expand Up @@ -170,7 +170,7 @@ export const RiskScoreField = ({
label={riskScoreMappingLabel}
labelAppend={field.labelAppend}
helpText={
isRiskScoreMappingSelected ? (
isRiskScoreMappingChecked ? (
<NestedContent>{i18n.RISK_SCORE_MAPPING_DETAILS}</NestedContent>
) : (
''
Expand All @@ -184,7 +184,7 @@ export const RiskScoreField = ({
>
<NestedContent>
<EuiSpacer size="s" />
{isRiskScoreMappingSelected && (
{isRiskScoreMappingChecked && (
<EuiFlexGroup direction={'column'} gutterSize="s">
<EuiFlexItem>
<EuiFlexGroup alignItems="center" gutterSize="s">
Expand All @@ -193,7 +193,7 @@ export const RiskScoreField = ({
</EuiFlexItem>
<EuiFlexItemIconColumn grow={false} />
<EuiFlexItemRiskScoreColumn grow={false}>
<EuiFormLabel>{i18n.RISK_SCORE}</EuiFormLabel>
<EuiFormLabel>{i18n.DEFAULT_RISK_SCORE}</EuiFormLabel>
</EuiFlexItemRiskScoreColumn>
</EuiFlexGroup>
</EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ import { i18n } from '@kbn/i18n';

export const RISK_SCORE = i18n.translate(
'xpack.securitySolution.alerts.riskScoreMapping.riskScoreTitle',
{
defaultMessage: 'Risk score',
}
);

export const DEFAULT_RISK_SCORE = i18n.translate(
'xpack.securitySolution.alerts.riskScoreMapping.defaultRiskScoreTitle',
{
defaultMessage: 'Default risk score',
}
Expand Down
Loading

0 comments on commit fc4c0e1

Please sign in to comment.