Skip to content

Commit

Permalink
[SIEM][Detection Engine] Critical blocker rule changes and ECS changes
Browse files Browse the repository at this point in the history
## Summary

* Changes ECS `techniques` to the word `technique` as `techniques` is incorrect ECS and incorrect mapping and without this our product could crash
* Changes ECS `threats` to the word `threat` as `threats` is incorrect ECS and incorrect mapping and without this our product could crash
* Added histogram mapping for `signal.rule.threat.tactic.name` as that was missing
* Added `Elastic` and removed `EIA` for tags
* Updates unit tests
* Cleans up rules by removing extra characters and removing fields not required.
* Adds concrete index'es as this was a critical breaking bug
* Fixes issues with imports where imports could change an immutable from false to true and suddenly cause out of band immutables to occur.

### Checklist

Use ~~strikethroughs~~ to remove checklist items you don't feel are applicable to this PR.

~~- [ ] This was checked for cross-browser compatibility, [including a check against IE11](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility)~~

~~- [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/master/packages/kbn-i18n/README.md)~~

~~- [ ] [Documentation](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#writing-documentation) was added for features that require explanation or tutorials~~

- [x] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios

~~- [ ] This was checked for [keyboard-only and screenreader accessibility](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility#Accessibility_testing_checklist)~~

### For maintainers

~~- [ ] This was checked for breaking API changes and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process)~~

- [x] This includes a feature addition or change that requires a release note and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process)
  • Loading branch information
FrankHassanabad authored Jan 25, 2020
1 parent f4b4695 commit a63e8a4
Show file tree
Hide file tree
Showing 203 changed files with 1,757 additions and 1,646 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const NewRuleSchema = t.intersection([
rule_id: t.string,
saved_id: t.string,
tags: t.array(t.string),
threats: t.array(t.unknown),
threat: t.array(t.unknown),
to: t.string,
updated_by: t.string,
}),
Expand Down Expand Up @@ -73,7 +73,7 @@ export const RuleSchema = t.intersection([
tags: t.array(t.string),
type: t.string,
to: t.string,
threats: t.array(t.unknown),
threat: t.array(t.unknown),
updated_at: t.string,
updated_by: t.string,
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4940,7 +4940,7 @@
"deprecationReason": null
},
{
"name": "threats",
"name": "threat",
"description": "",
"args": [],
"type": { "kind": "SCALAR", "name": "ToAny", "ofType": null },
Expand Down
2 changes: 1 addition & 1 deletion x-pack/legacy/plugins/siem/public/graphql/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1015,7 +1015,7 @@ export interface RuleField {

tags?: Maybe<string[]>;

threats?: Maybe<ToAny>;
threat?: Maybe<ToAny>;

type?: Maybe<string[]>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { SignalsHistogramOption } from './types';
export const signalsHistogramOptions: SignalsHistogramOption[] = [
{ text: 'signal.rule.risk_score', value: 'signal.rule.risk_score' },
{ text: 'signal.rule.severity', value: 'signal.rule.severity' },
{ text: 'signal.rule.threat.tactic.name', value: 'signal.rule.threat.tactic.name' },
{ text: 'destination.ip', value: 'destination.ip' },
{ text: 'event.action', value: 'event.action' },
{ text: 'event.category', value: 'event.category' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ export const tacticsOptions: MitreTacticsOptions[] = [
},
];

export const techniques = [
export const technique = [
{
name: '.bash_profile and .bashrc',
id: 'T1156',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const mockRule = (id: string): Rule => ({
tags: [],
to: 'now',
type: 'saved_query',
threats: [],
threat: [],
version: 1,
});

Expand Down Expand Up @@ -87,7 +87,7 @@ export const mockTableData: TableData[] = [
saved_id: "Garrett's IP",
severity: 'low',
tags: [],
threats: [],
threat: [],
timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
timeline_title: 'Untitled timeline',
to: 'now',
Expand Down Expand Up @@ -136,7 +136,7 @@ export const mockTableData: TableData[] = [
saved_id: "Garrett's IP",
severity: 'low',
tags: [],
threats: [],
threat: [],
timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
timeline_title: 'Untitled timeline',
to: 'now',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { tacticsOptions, techniquesOptions } from '../../../mitre/mitre_tactics_

import { FilterLabel } from './filter_label';
import * as i18n from './translations';
import { BuildQueryBarDescription, BuildThreatsDescription, ListItems } from './types';
import { BuildQueryBarDescription, BuildThreatDescription, ListItems } from './types';
import { SeverityBadge } from '../severity_badge';
import ListTreeIcon from './assets/list_tree_icon.svg';

Expand Down Expand Up @@ -94,7 +94,7 @@ export const buildQueryBarDescription = ({
return items;
};

const ThreatsEuiFlexGroup = styled(EuiFlexGroup)`
const ThreatEuiFlexGroup = styled(EuiFlexGroup)`
.euiFlexItem {
margin-bottom: 0px;
}
Expand All @@ -114,25 +114,22 @@ const ReferenceLinkItem = styled(EuiButtonEmpty)`
}
`;

export const buildThreatsDescription = ({
label,
threats,
}: BuildThreatsDescription): ListItems[] => {
if (threats.length > 0) {
export const buildThreatDescription = ({ label, threat }: BuildThreatDescription): ListItems[] => {
if (threat.length > 0) {
return [
{
title: label,
description: (
<ThreatsEuiFlexGroup direction="column">
{threats.map((threat, index) => {
const tactic = tacticsOptions.find(t => t.id === threat.tactic.id);
<ThreatEuiFlexGroup direction="column">
{threat.map((singleThreat, index) => {
const tactic = tacticsOptions.find(t => t.id === singleThreat.tactic.id);
return (
<EuiFlexItem key={`${threat.tactic.name}-${index}`}>
<EuiLink href={threat.tactic.reference} target="_blank">
<EuiFlexItem key={`${singleThreat.tactic.name}-${index}`}>
<EuiLink href={singleThreat.tactic.reference} target="_blank">
{tactic != null ? tactic.text : ''}
</EuiLink>
<EuiFlexGroup gutterSize="none" alignItems="flexStart" direction="column">
{threat.techniques.map(technique => {
{singleThreat.technique.map(technique => {
const myTechnique = techniquesOptions.find(t => t.id === technique.id);
return (
<EuiFlexItem>
Expand All @@ -153,7 +150,7 @@ export const buildThreatsDescription = ({
);
})}
<EuiSpacer />
</ThreatsEuiFlexGroup>
</ThreatEuiFlexGroup>
),
},
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
buildQueryBarDescription,
buildSeverityDescription,
buildStringArrayDescription,
buildThreatsDescription,
buildThreatDescription,
buildUnorderedListArrayDescription,
buildUrlsDescription,
} from './helpers';
Expand Down Expand Up @@ -116,11 +116,11 @@ const getDescriptionItem = (
savedId,
indexPatterns,
});
} else if (field === 'threats') {
const threats: IMitreEnterpriseAttack[] = get(field, value).filter(
(threat: IMitreEnterpriseAttack) => threat.tactic.name !== 'none'
} else if (field === 'threat') {
const threat: IMitreEnterpriseAttack[] = get(field, value).filter(
(singleThreat: IMitreEnterpriseAttack) => singleThreat.tactic.name !== 'none'
);
return buildThreatsDescription({ label, threats });
return buildThreatDescription({ label, threat });
} else if (field === 'description') {
return [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export interface BuildQueryBarDescription {
indexPatterns?: IIndexPattern;
}

export interface BuildThreatsDescription {
export interface BuildThreatDescription {
label: string;
threats: IMitreEnterpriseAttack[];
threat: IMitreEnterpriseAttack[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import { IMitreAttack } from '../../types';

export const isMitreAttackInvalid = (
tacticName: string | null | undefined,
techniques: IMitreAttack[] | null | undefined
technique: IMitreAttack[] | null | undefined
) => {
if (isEmpty(tacticName) || (tacticName !== 'none' && isEmpty(techniques))) {
if (isEmpty(tacticName) || (tacticName !== 'none' && isEmpty(technique))) {
return true;
}
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import styled from 'styled-components';
import { tacticsOptions, techniquesOptions } from '../../../mitre/mitre_tactics_techniques';
import * as Rulei18n from '../../translations';
import { FieldHook, getFieldValidityAndErrorMessage } from '../shared_imports';
import { threatsDefault } from '../step_about_rule/default_value';
import { threatDefault } from '../step_about_rule/default_value';
import { IMitreEnterpriseAttack } from '../../types';
import { MyAddItemButton } from '../add_item_form';
import { isMitreAttackInvalid } from './helpers';
Expand Down Expand Up @@ -49,7 +49,7 @@ export const AddMitreThreat = ({ dataTestSubj, field, idAria, isDisabled }: AddI
const values = field.value as string[];
const newValues = [...values.slice(0, index), ...values.slice(index + 1)];
if (isEmpty(newValues)) {
field.setValue(threatsDefault);
field.setValue(threatDefault);
} else {
field.setValue(newValues);
}
Expand All @@ -62,10 +62,10 @@ export const AddMitreThreat = ({ dataTestSubj, field, idAria, isDisabled }: AddI
if (!isEmpty(values[values.length - 1])) {
field.setValue([
...values,
{ tactic: { id: 'none', name: 'none', reference: 'none' }, techniques: [] },
{ tactic: { id: 'none', name: 'none', reference: 'none' }, technique: [] },
]);
} else {
field.setValue([{ tactic: { id: 'none', name: 'none', reference: 'none' }, techniques: [] }]);
field.setValue([{ tactic: { id: 'none', name: 'none', reference: 'none' }, technique: [] }]);
}
}, [field]);

Expand All @@ -82,7 +82,7 @@ export const AddMitreThreat = ({ dataTestSubj, field, idAria, isDisabled }: AddI
{
...values[index],
tactic: { id, reference, name },
techniques: [],
technique: [],
},
...values.slice(index + 1),
]);
Expand All @@ -96,7 +96,7 @@ export const AddMitreThreat = ({ dataTestSubj, field, idAria, isDisabled }: AddI
...values.slice(0, index),
{
...values[index],
techniques: selectedOptions,
technique: selectedOptions,
},
...values.slice(index + 1),
]);
Expand Down Expand Up @@ -133,9 +133,9 @@ export const AddMitreThreat = ({ dataTestSubj, field, idAria, isDisabled }: AddI
);

const getSelectTechniques = (item: IMitreEnterpriseAttack, index: number, disabled: boolean) => {
const invalid = isMitreAttackInvalid(item.tactic.name, item.techniques);
const invalid = isMitreAttackInvalid(item.tactic.name, item.technique);
const options = techniquesOptions.filter(t => t.tactics.includes(kebabCase(item.tactic.name)));
const selectedOptions = item.techniques.map(technic => ({
const selectedOptions = item.technique.map(technic => ({
...technic,
label: `${technic.name} (${technic.id})`, // API doesn't allow for label field
}));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const TACTIC = i18n.translate('xpack.siem.detectionEngine.mitreAttack.tac
export const TECHNIQUE = i18n.translate(
'xpack.siem.detectionEngine.mitreAttack.techniquesDescription',
{
defaultMessage: 'technique',
defaultMessage: 'techniques',
}
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
import { AboutStepRule } from '../../types';
import { DEFAULT_TIMELINE_TITLE } from '../../../../../components/timeline/search_super_select/translations';

export const threatsDefault = [
export const threatDefault = [
{
framework: 'MITRE ATT&CK',
tactic: { id: 'none', name: 'none', reference: 'none' },
techniques: [],
technique: [],
},
];

Expand All @@ -28,5 +28,5 @@ export const stepAboutDefaultValue: AboutStepRule = {
id: null,
title: DEFAULT_TIMELINE_TITLE,
},
threats: threatsDefault,
threat: threatDefault,
};
Original file line number Diff line number Diff line change
Expand Up @@ -227,12 +227,12 @@ const StepAboutRuleComponent: FC<StepAboutRuleProps> = ({
}}
/>
<UseField
path="threats"
path="threat"
component={AddMitreThreat}
componentProps={{
idAria: 'detectionEngineStepAboutRuleMitreThreats',
idAria: 'detectionEngineStepAboutRuleMitreThreat',
isDisabled: isLoading,
dataTestSubj: 'detectionEngineStepAboutRuleMitreThreats',
dataTestSubj: 'detectionEngineStepAboutRuleMitreThreat',
}}
/>
</AdvancedSettingsAccordion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export const schema: FormSchema = {
),
labelAppend: OptionalFieldLabel,
},
threats: {
threat: {
label: i18n.translate(
'xpack.siem.detectionEngine.createRule.stepAboutRule.fieldMitreThreatLabel',
{
Expand All @@ -155,7 +155,7 @@ export const schema: FormSchema = {
const [{ value, path }] = args;
let hasError = false;
(value as IMitreEnterpriseAttack[]).forEach(v => {
if (isMitreAttackInvalid(v.tactic.name, v.techniques)) {
if (isMitreAttackInvalid(v.tactic.name, v.technique)) {
hasError = true;
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,7 @@ const formatScheduleStepData = (scheduleData: ScheduleStepRule): ScheduleStepRul
};

const formatAboutStepData = (aboutStepData: AboutStepRule): AboutStepRuleJson => {
const {
falsePositives,
references,
riskScore,
threats,
timeline,
isNew,
...rest
} = aboutStepData;
const { falsePositives, references, riskScore, threat, timeline, isNew, ...rest } = aboutStepData;
return {
false_positives: falsePositives.filter(item => !isEmpty(item)),
references: references.filter(item => !isEmpty(item)),
Expand All @@ -91,12 +83,12 @@ const formatAboutStepData = (aboutStepData: AboutStepRule): AboutStepRuleJson =>
timeline_title: timeline.title,
}
: {}),
threats: threats
.filter(threat => threat.tactic.name !== 'none')
.map(threat => ({
...threat,
threat: threat
.filter(singleThreat => singleThreat.tactic.name !== 'none')
.map(singleThreat => ({
...singleThreat,
framework: 'MITRE ATT&CK',
techniques: threat.techniques.map(technique => {
technique: singleThreat.technique.map(technique => {
const { id, name, reference } = technique;
return { id, name, reference };
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ export const getStepsData = ({
rule != null
? {
isNew: false,
...pick(['description', 'name', 'references', 'severity', 'tags', 'threats'], rule),
...pick(['description', 'name', 'references', 'severity', 'tags', 'threat'], rule),
...(detailsView ? { name: '' } : {}),
threats: rule.threats as IMitreEnterpriseAttack[],
threat: rule.threat as IMitreEnterpriseAttack[],
falsePositives: rule.false_positives,
riskScore: rule.risk_score,
timeline: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export interface AboutStepRule extends StepRuleData {
falsePositives: string[];
tags: string[];
timeline: FieldValueTimeline;
threats: IMitreEnterpriseAttack[];
threat: IMitreEnterpriseAttack[];
}

export interface DefineStepRule extends StepRuleData {
Expand Down Expand Up @@ -109,7 +109,7 @@ export interface AboutStepRuleJson {
tags: string[];
timeline_id?: string;
timeline_title?: string;
threats: IMitreEnterpriseAttack[];
threat: IMitreEnterpriseAttack[];
}

export interface ScheduleStepRuleJson {
Expand All @@ -134,5 +134,5 @@ export interface IMitreAttack {
export interface IMitreEnterpriseAttack {
framework: string;
tactic: IMitreAttack;
techniques: IMitreAttack[];
technique: IMitreAttack[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const SignalsByCategory = React.memo<Props>(
showLinkToSignals={true}
defaultStackByOption={{
text: `${i18n.SIGNALS_BY_CATEGORY}`,
value: 'signal.rule.threats',
value: 'signal.rule.threat',
}}
legendPosition={'right'}
to={to}
Expand Down
Loading

0 comments on commit a63e8a4

Please sign in to comment.