diff --git a/x-pack/legacy/plugins/siem/public/components/ml/influencers/create_influencers.test.tsx b/x-pack/legacy/plugins/siem/public/components/ml/influencers/create_influencers.test.tsx
index a79ebdbc5ed60..615e83d208dd6 100644
--- a/x-pack/legacy/plugins/siem/public/components/ml/influencers/create_influencers.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/ml/influencers/create_influencers.test.tsx
@@ -19,10 +19,15 @@ describe('create_influencers', () => {
});
test('renders correctly against snapshot', () => {
- const wrapper = shallow({createInfluencers(anomalies.anomalies[0])});
+ const wrapper = shallow({createInfluencers(anomalies.anomalies[0].influencers)});
expect(toJson(wrapper)).toMatchSnapshot();
});
+ test('it returns an empty string when influencers is undefined', () => {
+ const wrapper = mount({createInfluencers()});
+ expect(wrapper.text()).toEqual('');
+ });
+
test('it returns expected createKeyAndValue record with special left and right quotes', () => {
const entities = createKeyAndValue({ 'name-1': 'value-1' });
expect(entities).toEqual('name-1: "value-1"');
@@ -34,13 +39,13 @@ describe('create_influencers', () => {
});
test('it creates the anomalies without filtering anything out since they are all well formed', () => {
- const wrapper = mount({createInfluencers(anomalies.anomalies[0])});
+ const wrapper = mount({createInfluencers(anomalies.anomalies[0].influencers)});
expect(wrapper.text()).toEqual('host.name: "zeek-iowa"process.name: "du"user.name: "root"');
});
test('it returns empty text when passed in empty objects of influencers', () => {
anomalies.anomalies[0].influencers = [{}, {}, {}];
- const wrapper = mount({createInfluencers(anomalies.anomalies[0])});
+ const wrapper = mount({createInfluencers(anomalies.anomalies[0].influencers)});
expect(wrapper.text()).toEqual('');
});
@@ -50,7 +55,7 @@ describe('create_influencers', () => {
{},
{ 'influencer-name-two': 'influencer-value-two' },
];
- const wrapper = mount({createInfluencers(anomalies.anomalies[0])});
+ const wrapper = mount({createInfluencers(anomalies.anomalies[0].influencers)});
expect(wrapper.text()).toEqual(
'influencer-name-one: "influencer-value-one"influencer-name-two: "influencer-value-two"'
);
diff --git a/x-pack/legacy/plugins/siem/public/components/ml/influencers/create_influencers.tsx b/x-pack/legacy/plugins/siem/public/components/ml/influencers/create_influencers.tsx
index 010353f2c1e12..3418cad8d5d71 100644
--- a/x-pack/legacy/plugins/siem/public/components/ml/influencers/create_influencers.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/ml/influencers/create_influencers.tsx
@@ -7,7 +7,6 @@
import { EuiFlexItem } from '@elastic/eui';
import React from 'react';
import { isEmpty } from 'lodash/fp';
-import { Anomaly } from '../types';
import { getEntries } from '../get_entries';
export const createKeyAndValue = (influencer: Record): string => {
@@ -19,19 +18,14 @@ export const createKeyAndValue = (influencer: Record): string =>
}
};
-export const createInfluencers = (score: Anomaly): JSX.Element => {
- return (
- <>
- {score.influencers
- .filter(influencer => !isEmpty(influencer))
- .map(influencer => {
- const keyAndValue = createKeyAndValue(influencer);
- return (
-
- {keyAndValue}
-
- );
- })}
- >
- );
-};
+export const createInfluencers = (influencers: Array> = []): JSX.Element[] =>
+ influencers
+ .filter(influencer => !isEmpty(influencer))
+ .map(influencer => {
+ const keyAndValue = createKeyAndValue(influencer);
+ return (
+
+ {keyAndValue}
+
+ );
+ });
diff --git a/x-pack/legacy/plugins/siem/public/components/ml/influencers/get_host_name_from_influencers.test.ts b/x-pack/legacy/plugins/siem/public/components/ml/influencers/get_host_name_from_influencers.test.ts
index 5913dec4277af..727be435d712a 100644
--- a/x-pack/legacy/plugins/siem/public/components/ml/influencers/get_host_name_from_influencers.test.ts
+++ b/x-pack/legacy/plugins/siem/public/components/ml/influencers/get_host_name_from_influencers.test.ts
@@ -26,6 +26,11 @@ describe('get_host_name_from_influencers', () => {
expect(hostName).toEqual(null);
});
+ test('returns null if it is given undefined influencers', () => {
+ const hostName = getHostNameFromInfluencers();
+ expect(hostName).toEqual(null);
+ });
+
test('returns null if there influencers is an empty object', () => {
anomalies.anomalies[0].influencers = [{}];
const hostName = getHostNameFromInfluencers(anomalies.anomalies[0].influencers);
diff --git a/x-pack/legacy/plugins/siem/public/components/ml/influencers/get_host_name_from_influencers.ts b/x-pack/legacy/plugins/siem/public/components/ml/influencers/get_host_name_from_influencers.ts
index 6507c15d4e407..54b825d4b9426 100644
--- a/x-pack/legacy/plugins/siem/public/components/ml/influencers/get_host_name_from_influencers.ts
+++ b/x-pack/legacy/plugins/siem/public/components/ml/influencers/get_host_name_from_influencers.ts
@@ -7,7 +7,7 @@
import { getEntries } from '../get_entries';
export const getHostNameFromInfluencers = (
- influencers: Array>,
+ influencers: Array> = [],
hostName?: string
): string | null => {
const recordFound = influencers.find(influencer => {
diff --git a/x-pack/legacy/plugins/siem/public/components/ml/influencers/get_network_from_influencers.test.ts b/x-pack/legacy/plugins/siem/public/components/ml/influencers/get_network_from_influencers.test.ts
index fa3b5e2c4e326..0ed3c3844a5b8 100644
--- a/x-pack/legacy/plugins/siem/public/components/ml/influencers/get_network_from_influencers.test.ts
+++ b/x-pack/legacy/plugins/siem/public/components/ml/influencers/get_network_from_influencers.test.ts
@@ -28,6 +28,11 @@ describe('get_network_from_influencers', () => {
expect(network).toEqual(null);
});
+ test('returns null if the influencers are undefined', () => {
+ const network = getNetworkFromInfluencers();
+ expect(network).toEqual(null);
+ });
+
test('returns network name of source mixed with other data', () => {
anomalies.anomalies[0].influencers = [{ 'host.name': 'name-1' }, { 'source.ip': '127.0.0.1' }];
const network = getNetworkFromInfluencers(anomalies.anomalies[0].influencers);
diff --git a/x-pack/legacy/plugins/siem/public/components/ml/influencers/get_network_from_influencers.ts b/x-pack/legacy/plugins/siem/public/components/ml/influencers/get_network_from_influencers.ts
index 2ea5cad63b1bf..16e1be05d0299 100644
--- a/x-pack/legacy/plugins/siem/public/components/ml/influencers/get_network_from_influencers.ts
+++ b/x-pack/legacy/plugins/siem/public/components/ml/influencers/get_network_from_influencers.ts
@@ -8,7 +8,7 @@ import { DestinationOrSource, isDestinationOrSource } from '../types';
import { getEntries } from '../get_entries';
export const getNetworkFromInfluencers = (
- influencers: Array>,
+ influencers: Array> = [],
ip?: string
): { ip: string; type: DestinationOrSource } | null => {
const recordFound = influencers.find(influencer => {
diff --git a/x-pack/legacy/plugins/siem/public/components/ml/score/__snapshots__/anomaly_score.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/ml/score/__snapshots__/anomaly_score.test.tsx.snap
index e0b5714d74c59..e620507d2001d 100644
--- a/x-pack/legacy/plugins/siem/public/components/ml/score/__snapshots__/anomaly_score.test.tsx.snap
+++ b/x-pack/legacy/plugins/siem/public/components/ml/score/__snapshots__/anomaly_score.test.tsx.snap
@@ -191,23 +191,21 @@ exports[`anomaly_scores renders correctly against snapshot 1`] = `
gutterSize="none"
responsive={false}
>
-
-
- host.name: "zeek-iowa"
-
-
- process.name: "du"
-
-
- user.name: "root"
-
-
+
+ host.name: "zeek-iowa"
+
+
+ process.name: "du"
+
+
+ user.name: "root"
+
,
"title": "Influenced By",
},
diff --git a/x-pack/legacy/plugins/siem/public/components/ml/score/create_description_list.tsx b/x-pack/legacy/plugins/siem/public/components/ml/score/create_description_list.tsx
index 81aa58a0d0368..da07337653dc0 100644
--- a/x-pack/legacy/plugins/siem/public/components/ml/score/create_description_list.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/ml/score/create_description_list.tsx
@@ -88,7 +88,7 @@ export const createDescriptionList = (
title: i18n.INFLUENCED_BY,
description: (
- {createInfluencers(score)}
+ {createInfluencers(score.influencers)}
),
},
diff --git a/x-pack/legacy/plugins/siem/public/components/ml/score/create_entities_from_score.test.ts b/x-pack/legacy/plugins/siem/public/components/ml/score/create_entities_from_score.test.ts
index 4340bcaf274d4..09ce472fe8a7b 100644
--- a/x-pack/legacy/plugins/siem/public/components/ml/score/create_entities_from_score.test.ts
+++ b/x-pack/legacy/plugins/siem/public/components/ml/score/create_entities_from_score.test.ts
@@ -9,6 +9,7 @@ import {
createEntitiesFromScore,
createEntity,
createEntityFromRecord,
+ createInfluencersFromScore,
} from './create_entities_from_score';
import { cloneDeep } from 'lodash/fp';
@@ -68,4 +69,41 @@ describe('create_entities_from_score', () => {
const entity = createEntityFromRecord({ 'name-1': 'value-1' });
expect(entity).toEqual("name-1:'value-1'");
});
+
+ test('it returns expected entities from a typical score for influencers', () => {
+ const influencers = createInfluencersFromScore(anomalies.anomalies[0].influencers);
+ expect(influencers).toEqual("host.name:'zeek-iowa',process.name:'du',user.name:'root'");
+ });
+
+ test('it returns empty string for empty influencers', () => {
+ const influencers = createInfluencersFromScore([]);
+ expect(influencers).toEqual('');
+ });
+
+ test('it returns empty string for undefined influencers', () => {
+ const influencers = createInfluencersFromScore();
+ expect(influencers).toEqual('');
+ });
+
+ test('it returns single influencer', () => {
+ const influencers = createInfluencersFromScore([{ 'influencer-1': 'value-1' }]);
+ expect(influencers).toEqual("influencer-1:'value-1'");
+ });
+
+ test('it returns two influencers', () => {
+ const influencers = createInfluencersFromScore([
+ { 'influencer-1': 'value-1' },
+ { 'influencer-2': 'value-2' },
+ ]);
+ expect(influencers).toEqual("influencer-1:'value-1',influencer-2:'value-2'");
+ });
+
+ test('it creates a simple string entity with undefined influencers', () => {
+ const anomaly = anomalies.anomalies[0];
+ anomaly.entityName = 'name-1';
+ anomaly.entityValue = 'value-1';
+ delete anomaly.influencers;
+ const entities = createEntitiesFromScore(anomaly);
+ expect(entities).toEqual("name-1:'value-1'");
+ });
});
diff --git a/x-pack/legacy/plugins/siem/public/components/ml/score/create_entities_from_score.ts b/x-pack/legacy/plugins/siem/public/components/ml/score/create_entities_from_score.ts
index 219d408598a6b..f2db3b8a6d57b 100644
--- a/x-pack/legacy/plugins/siem/public/components/ml/score/create_entities_from_score.ts
+++ b/x-pack/legacy/plugins/siem/public/components/ml/score/create_entities_from_score.ts
@@ -12,8 +12,10 @@ export const createEntityFromRecord = (entity: Record): string =
export const createEntity = (entityName: string, entityValue: string): string =>
`${entityName}:'${entityValue}'`;
-export const createEntitiesFromScore = (score: Anomaly): string => {
- const influencers = score.influencers.reduce((accum, item, index) => {
+export const createInfluencersFromScore = (
+ influencers: Array> = []
+): string =>
+ influencers.reduce((accum, item, index) => {
if (index === 0) {
return createEntityFromRecord(item);
} else {
@@ -21,6 +23,9 @@ export const createEntitiesFromScore = (score: Anomaly): string => {
}
}, '');
+export const createEntitiesFromScore = (score: Anomaly): string => {
+ const influencers = createInfluencersFromScore(score.influencers);
+
if (influencers.length === 0) {
return createEntity(score.entityName, score.entityValue);
} else if (!influencers.includes(score.entityName)) {
diff --git a/x-pack/legacy/plugins/siem/public/components/ml/tables/get_anomalies_host_table_columns.test.tsx b/x-pack/legacy/plugins/siem/public/components/ml/tables/get_anomalies_host_table_columns.test.tsx
index 5aeeee6d58304..3a1fcbb653efe 100644
--- a/x-pack/legacy/plugins/siem/public/components/ml/tables/get_anomalies_host_table_columns.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/ml/tables/get_anomalies_host_table_columns.test.tsx
@@ -7,7 +7,7 @@
import { getAnomaliesHostTableColumnsCurated } from './get_anomalies_host_table_columns';
import { HostsType } from '../../../store/hosts/model';
import * as i18n from './translations';
-import { AnomaliesByHost } from '../types';
+import { AnomaliesByHost, Anomaly } from '../types';
import { Columns } from '../../load_more_table';
import { TestProviders } from '../../../mock';
import { mount } from 'enzyme';
@@ -96,7 +96,7 @@ describe('get_anomalies_host_table_columns', () => {
is_interim: true,
timestamp: new Date('01/01/2000').valueOf(),
by_field_name: 'some field name',
- by_field_value: 'some field valuke',
+ by_field_value: 'some field value',
partition_field_name: 'partition field name',
partition_field_value: 'partition field value',
function: 'function-1',
@@ -121,4 +121,57 @@ describe('get_anomalies_host_table_columns', () => {
expect(column).not.toBe(null);
}
});
+
+ test('on host page, undefined influencers should turn into an empty column string', () => {
+ const columns = getAnomaliesHostTableColumnsCurated(
+ HostsType.page,
+ startDate,
+ endDate,
+ interval,
+ narrowDateRange
+ );
+ const column = columns.find(col => col.name === i18n.INFLUENCED_BY) as Columns<
+ Anomaly['influencers'],
+ AnomaliesByHost
+ >;
+ const anomaly: AnomaliesByHost = {
+ hostName: 'host.name',
+ anomaly: {
+ detectorIndex: 0,
+ entityName: 'entity-name-1',
+ entityValue: 'entity-value-1',
+ jobId: 'job-1',
+ rowId: 'row-1',
+ severity: 100,
+ time: new Date('01/01/2000').valueOf(),
+ source: {
+ job_id: 'job-1',
+ result_type: 'result-1',
+ probability: 50,
+ multi_bucket_impact: 0,
+ record_score: 0,
+ initial_record_score: 0,
+ bucket_span: 0,
+ detector_index: 0,
+ is_interim: true,
+ timestamp: new Date('01/01/2000').valueOf(),
+ by_field_name: 'some field name',
+ by_field_value: 'some field value',
+ partition_field_name: 'partition field name',
+ partition_field_value: 'partition field value',
+ function: 'function-1',
+ function_description: 'description-1',
+ typical: [5, 3],
+ actual: [7, 4],
+ influencers: [],
+ },
+ },
+ };
+ if (column != null && column.render != null) {
+ const wrapper = mount({column.render(undefined, anomaly)});
+ expect(wrapper.text()).toEqual('');
+ } else {
+ expect(column).not.toBe(null);
+ }
+ });
});
diff --git a/x-pack/legacy/plugins/siem/public/components/ml/tables/get_anomalies_host_table_columns.tsx b/x-pack/legacy/plugins/siem/public/components/ml/tables/get_anomalies_host_table_columns.tsx
index ef829cadb9fb6..97d09c3e74b06 100644
--- a/x-pack/legacy/plugins/siem/public/components/ml/tables/get_anomalies_host_table_columns.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/ml/tables/get_anomalies_host_table_columns.tsx
@@ -95,29 +95,30 @@ export const getAnomaliesHostTableColumns = (
field: 'anomaly.influencers',
render: (influencers, anomaliesByHost) => (
- {influencers.map(influencer => {
- const [key, value] = getEntries(influencer);
- const entityName = key != null ? key : '';
- const entityValue = value != null ? value : '';
- return (
-
-
-
-
-
-
-
- );
- })}
+ {influencers &&
+ influencers.map(influencer => {
+ const [key, value] = getEntries(influencer);
+ const entityName = key != null ? key : '';
+ const entityValue = value != null ? value : '';
+ return (
+
+
+
+
+
+
+
+ );
+ })}
),
},
diff --git a/x-pack/legacy/plugins/siem/public/components/ml/tables/get_anomalies_network_table_columns.test.tsx b/x-pack/legacy/plugins/siem/public/components/ml/tables/get_anomalies_network_table_columns.test.tsx
index 706c4feaa0f37..d063ed023bca6 100644
--- a/x-pack/legacy/plugins/siem/public/components/ml/tables/get_anomalies_network_table_columns.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/ml/tables/get_anomalies_network_table_columns.test.tsx
@@ -7,7 +7,7 @@
import { getAnomaliesNetworkTableColumnsCurated } from './get_anomalies_network_table_columns';
import { NetworkType } from '../../../store/network/model';
import * as i18n from './translations';
-import { AnomaliesByNetwork } from '../types';
+import { AnomaliesByNetwork, Anomaly } from '../types';
import { Columns } from '../../load_more_table';
import { mount } from 'enzyme';
import React from 'react';
@@ -100,7 +100,7 @@ describe('get_anomalies_network_table_columns', () => {
is_interim: true,
timestamp: new Date('01/01/2000').valueOf(),
by_field_name: 'some field name',
- by_field_value: 'some field valuke',
+ by_field_value: 'some field value',
partition_field_name: 'partition field name',
partition_field_value: 'partition field value',
function: 'function-1',
@@ -125,4 +125,58 @@ describe('get_anomalies_network_table_columns', () => {
expect(column).not.toBe(null);
}
});
+
+ test('on network page, undefined influencers should turn into an empty column string', () => {
+ const columns = getAnomaliesNetworkTableColumnsCurated(
+ NetworkType.page,
+ startDate,
+ endDate,
+ interval,
+ narrowDateRange
+ );
+ const column = columns.find(col => col.name === i18n.INFLUENCED_BY) as Columns<
+ Anomaly['influencers'],
+ AnomaliesByNetwork
+ >;
+ const anomaly: AnomaliesByNetwork = {
+ type: 'source.ip',
+ ip: '127.0.0.1',
+ anomaly: {
+ detectorIndex: 0,
+ entityName: 'entity-name-1',
+ entityValue: 'entity-value-1',
+ jobId: 'job-1',
+ rowId: 'row-1',
+ severity: 100,
+ time: new Date('01/01/2000').valueOf(),
+ source: {
+ job_id: 'job-1',
+ result_type: 'result-1',
+ probability: 50,
+ multi_bucket_impact: 0,
+ record_score: 0,
+ initial_record_score: 0,
+ bucket_span: 0,
+ detector_index: 0,
+ is_interim: true,
+ timestamp: new Date('01/01/2000').valueOf(),
+ by_field_name: 'some field name',
+ by_field_value: 'some field value',
+ partition_field_name: 'partition field name',
+ partition_field_value: 'partition field value',
+ function: 'function-1',
+ function_description: 'description-1',
+ typical: [5, 3],
+ actual: [7, 4],
+ influencers: [],
+ },
+ },
+ };
+ if (column != null && column.render != null) {
+ const wrapper = mount({column.render(undefined, anomaly)});
+ expect(wrapper.text()).toEqual('');
+ } else {
+ expect(column).not.toBe(null);
+ }
+ });
});
diff --git a/x-pack/legacy/plugins/siem/public/components/ml/tables/get_anomalies_network_table_columns.tsx b/x-pack/legacy/plugins/siem/public/components/ml/tables/get_anomalies_network_table_columns.tsx
index 771f6f50857ae..4792c9343eae6 100644
--- a/x-pack/legacy/plugins/siem/public/components/ml/tables/get_anomalies_network_table_columns.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/ml/tables/get_anomalies_network_table_columns.tsx
@@ -93,25 +93,26 @@ export const getAnomaliesNetworkTableColumns = (
field: 'anomaly.influencers',
render: (influencers, anomaliesByNetwork) => (
- {influencers.map(influencer => {
- const [key, value] = getEntries(influencer);
- const entityName = key != null ? key : '';
- const entityValue = value != null ? value : '';
- return (
-
-
-
- );
- })}
+ {influencers &&
+ influencers.map(influencer => {
+ const [key, value] = getEntries(influencer);
+ const entityName = key != null ? key : '';
+ const entityValue = value != null ? value : '';
+ return (
+
+
+
+ );
+ })}
),
},
diff --git a/x-pack/legacy/plugins/siem/public/components/ml/types.ts b/x-pack/legacy/plugins/siem/public/components/ml/types.ts
index d76b2258d088c..75780a659e245 100644
--- a/x-pack/legacy/plugins/siem/public/components/ml/types.ts
+++ b/x-pack/legacy/plugins/siem/public/components/ml/types.ts
@@ -54,7 +54,7 @@ export interface Anomaly {
detectorIndex: number;
entityName: string;
entityValue: string;
- influencers: Array>;
+ influencers?: Array>;
jobId: string;
rowId: string;
severity: number;