[Security Solution] [Timeline] Bugfix to include unmapped fields in the timeline event details JSON (#92025)
This commit is contained in:
parent
7a1944a5a0
commit
90976ee119
35 changed files with 1248 additions and 403 deletions
|
@ -16,6 +16,7 @@ export interface TimelineEventsDetailsItem {
|
|||
values?: Maybe<string[]>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
originalValue?: Maybe<any>;
|
||||
isObjectArray: boolean;
|
||||
}
|
||||
|
||||
export interface TimelineEventsDetailsStrategyResponse extends IEsSearchResponse {
|
||||
|
|
|
@ -15,8 +15,8 @@ exports[`JSON View rendering should match snapshot 1`] = `
|
|||
value="{
|
||||
\\"_id\\": \\"pEMaMmkBUV60JmNWmWVi\\",
|
||||
\\"_index\\": \\"filebeat-8.0.0-2019.02.19-000001\\",
|
||||
\\"_type\\": \\"_doc\\",
|
||||
\\"_score\\": 1,
|
||||
\\"_type\\": \\"_doc\\",
|
||||
\\"@timestamp\\": \\"2019-02-28T16:50:54.621Z\\",
|
||||
\\"agent\\": {
|
||||
\\"ephemeral_id\\": \\"9d391ef2-a734-4787-8891-67031178c641\\",
|
||||
|
|
|
@ -85,24 +85,28 @@ export const getColumns = ({
|
|||
sortable: false,
|
||||
truncateText: false,
|
||||
width: '30px',
|
||||
render: (field: string) => (
|
||||
<EuiToolTip content={i18n.VIEW_COLUMN(field)}>
|
||||
<EuiCheckbox
|
||||
aria-label={i18n.VIEW_COLUMN(field)}
|
||||
checked={columnHeaders.findIndex((c) => c.id === field) !== -1}
|
||||
data-test-subj={`toggle-field-${field}`}
|
||||
data-colindex={1}
|
||||
id={field}
|
||||
onChange={() =>
|
||||
toggleColumn({
|
||||
columnHeaderType: defaultColumnHeaderType,
|
||||
id: field,
|
||||
width: DEFAULT_COLUMN_MIN_WIDTH,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</EuiToolTip>
|
||||
),
|
||||
render: (field: string, data: EventFieldsData) => {
|
||||
const label = data.isObjectArray ? i18n.NESTED_COLUMN(field) : i18n.VIEW_COLUMN(field);
|
||||
return (
|
||||
<EuiToolTip content={label}>
|
||||
<EuiCheckbox
|
||||
aria-label={label}
|
||||
checked={columnHeaders.findIndex((c) => c.id === field) !== -1}
|
||||
data-test-subj={`toggle-field-${field}`}
|
||||
data-colindex={1}
|
||||
id={field}
|
||||
onChange={() =>
|
||||
toggleColumn({
|
||||
columnHeaderType: defaultColumnHeaderType,
|
||||
id: field,
|
||||
width: DEFAULT_COLUMN_MIN_WIDTH,
|
||||
})
|
||||
}
|
||||
disabled={data.isObjectArray && data.type !== 'geo_point'}
|
||||
/>
|
||||
</EuiToolTip>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'field',
|
||||
|
@ -118,38 +122,42 @@ export const getColumns = ({
|
|||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<DroppableWrapper
|
||||
droppableId={getDroppableId(
|
||||
`event-details-field-droppable-wrapper-${contextId}-${eventId}-${data.category}-${field}`
|
||||
)}
|
||||
key={getDroppableId(
|
||||
`event-details-field-droppable-wrapper-${contextId}-${eventId}-${data.category}-${field}`
|
||||
)}
|
||||
isDropDisabled={true}
|
||||
type={DRAG_TYPE_FIELD}
|
||||
renderClone={(provided) => (
|
||||
<div
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
ref={provided.innerRef}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<DragEffects>
|
||||
<DraggableFieldBadge fieldId={field} />
|
||||
</DragEffects>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<DraggableFieldsBrowserField
|
||||
browserFields={browserFields}
|
||||
categoryId={data.category}
|
||||
fieldName={field}
|
||||
fieldCategory={data.category}
|
||||
onUpdateColumns={onUpdateColumns}
|
||||
timelineId={timelineId}
|
||||
toggleColumn={toggleColumn}
|
||||
/>
|
||||
</DroppableWrapper>
|
||||
{data.isObjectArray && data.type !== 'geo_point' ? (
|
||||
<>{field}</>
|
||||
) : (
|
||||
<DroppableWrapper
|
||||
droppableId={getDroppableId(
|
||||
`event-details-field-droppable-wrapper-${contextId}-${eventId}-${data.category}-${field}`
|
||||
)}
|
||||
key={getDroppableId(
|
||||
`event-details-field-droppable-wrapper-${contextId}-${eventId}-${data.category}-${field}`
|
||||
)}
|
||||
isDropDisabled={true}
|
||||
type={DRAG_TYPE_FIELD}
|
||||
renderClone={(provided) => (
|
||||
<div
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
ref={provided.innerRef}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<DragEffects>
|
||||
<DraggableFieldBadge fieldId={field} />
|
||||
</DragEffects>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<DraggableFieldsBrowserField
|
||||
browserFields={browserFields}
|
||||
categoryId={data.category}
|
||||
fieldName={field}
|
||||
fieldCategory={data.category}
|
||||
onUpdateColumns={onUpdateColumns}
|
||||
timelineId={timelineId}
|
||||
toggleColumn={toggleColumn}
|
||||
/>
|
||||
</DroppableWrapper>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiIconTip
|
||||
|
@ -191,6 +199,7 @@ export const getColumns = ({
|
|||
fieldFormat={data.format}
|
||||
fieldName={data.field}
|
||||
fieldType={data.type}
|
||||
isObjectArray={data.isObjectArray}
|
||||
value={value}
|
||||
linkValue={getLinkValue(data.field)}
|
||||
/>
|
||||
|
|
|
@ -108,7 +108,6 @@ export const EventFieldsBrowser = React.memo<Props>(
|
|||
|
||||
const columnHeaders = useDeepEqualSelector((state) => {
|
||||
const { columns } = getTimeline(state, timelineId) ?? timelineDefaults;
|
||||
|
||||
return getColumnHeaders(columns, browserFields);
|
||||
});
|
||||
|
||||
|
|
|
@ -112,6 +112,7 @@ export const getIconFromType = (type: string | null) => {
|
|||
case 'date':
|
||||
return 'clock';
|
||||
case 'ip':
|
||||
case 'geo_point':
|
||||
return 'globe';
|
||||
case 'object':
|
||||
return 'questionInCircle';
|
||||
|
|
|
@ -54,12 +54,14 @@ export const JsonView = React.memo<Props>(({ data }) => {
|
|||
JsonView.displayName = 'JsonView';
|
||||
|
||||
export const buildJsonView = (data: TimelineEventsDetailsItem[]) =>
|
||||
data.reduce(
|
||||
(accumulator, item) =>
|
||||
set(
|
||||
item.field,
|
||||
Array.isArray(item.originalValue) ? item.originalValue.join() : item.originalValue,
|
||||
accumulator
|
||||
),
|
||||
{}
|
||||
);
|
||||
data
|
||||
.sort((a, b) => a.field.localeCompare(b.field))
|
||||
.reduce(
|
||||
(accumulator, item) =>
|
||||
set(
|
||||
item.field,
|
||||
Array.isArray(item.originalValue) ? item.originalValue.join() : item.originalValue,
|
||||
accumulator
|
||||
),
|
||||
{}
|
||||
);
|
||||
|
|
|
@ -61,3 +61,10 @@ export const VIEW_COLUMN = (field: string) =>
|
|||
values: { field },
|
||||
defaultMessage: 'View {field} column',
|
||||
});
|
||||
|
||||
export const NESTED_COLUMN = (field: string) =>
|
||||
i18n.translate('xpack.securitySolution.eventDetails.nestedColumnCheckboxAriaLabel', {
|
||||
values: { field },
|
||||
defaultMessage:
|
||||
'The {field} field is an object, and is broken down into nested fields which can be added as column',
|
||||
});
|
||||
|
|
|
@ -14,105 +14,126 @@ export const mockDetailItemData: TimelineEventsDetailsItem[] = [
|
|||
field: '_id',
|
||||
originalValue: 'pEMaMmkBUV60JmNWmWVi',
|
||||
values: ['pEMaMmkBUV60JmNWmWVi'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
field: '_index',
|
||||
originalValue: 'filebeat-8.0.0-2019.02.19-000001',
|
||||
values: ['filebeat-8.0.0-2019.02.19-000001'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
field: '_type',
|
||||
originalValue: '_doc',
|
||||
values: ['_doc'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
field: '_score',
|
||||
originalValue: 1,
|
||||
values: ['1'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
field: '@timestamp',
|
||||
originalValue: '2019-02-28T16:50:54.621Z',
|
||||
values: ['2019-02-28T16:50:54.621Z'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
field: 'agent.ephemeral_id',
|
||||
originalValue: '9d391ef2-a734-4787-8891-67031178c641',
|
||||
values: ['9d391ef2-a734-4787-8891-67031178c641'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
field: 'agent.hostname',
|
||||
originalValue: 'siem-kibana',
|
||||
values: ['siem-kibana'],
|
||||
},
|
||||
{
|
||||
field: 'agent.id',
|
||||
originalValue: '5de03d5f-52f3-482e-91d4-853c7de073c3',
|
||||
values: ['5de03d5f-52f3-482e-91d4-853c7de073c3'],
|
||||
},
|
||||
{
|
||||
field: 'agent.type',
|
||||
originalValue: 'filebeat',
|
||||
values: ['filebeat'],
|
||||
},
|
||||
{
|
||||
field: 'agent.version',
|
||||
originalValue: '8.0.0',
|
||||
values: ['8.0.0'],
|
||||
},
|
||||
{
|
||||
field: 'cloud.availability_zone',
|
||||
originalValue: 'projects/189716325846/zones/us-east1-b',
|
||||
values: ['projects/189716325846/zones/us-east1-b'],
|
||||
},
|
||||
{
|
||||
field: 'cloud.instance.id',
|
||||
originalValue: '5412578377715150143',
|
||||
values: ['5412578377715150143'],
|
||||
},
|
||||
{
|
||||
field: 'cloud.instance.name',
|
||||
originalValue: 'siem-kibana',
|
||||
values: ['siem-kibana'],
|
||||
},
|
||||
{
|
||||
field: 'cloud.machine.type',
|
||||
originalValue: 'projects/189716325846/machineTypes/n1-standard-1',
|
||||
values: ['projects/189716325846/machineTypes/n1-standard-1'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
field: 'cloud.project.id',
|
||||
originalValue: 'elastic-beats',
|
||||
values: ['elastic-beats'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
field: 'cloud.provider',
|
||||
originalValue: 'gce',
|
||||
values: ['gce'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
field: 'destination.bytes',
|
||||
originalValue: 584,
|
||||
values: ['584'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
field: 'destination.ip',
|
||||
originalValue: '10.47.8.200',
|
||||
values: ['10.47.8.200'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
field: 'agent.id',
|
||||
originalValue: '5de03d5f-52f3-482e-91d4-853c7de073c3',
|
||||
values: ['5de03d5f-52f3-482e-91d4-853c7de073c3'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
field: 'cloud.instance.name',
|
||||
originalValue: 'siem-kibana',
|
||||
values: ['siem-kibana'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
field: 'cloud.machine.type',
|
||||
originalValue: 'projects/189716325846/machineTypes/n1-standard-1',
|
||||
values: ['projects/189716325846/machineTypes/n1-standard-1'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
field: 'agent.type',
|
||||
originalValue: 'filebeat',
|
||||
values: ['filebeat'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
field: 'destination.packets',
|
||||
originalValue: 4,
|
||||
values: ['4'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
field: 'destination.port',
|
||||
originalValue: 902,
|
||||
values: ['902'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
field: 'event.kind',
|
||||
originalValue: 'event',
|
||||
values: ['event'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
field: 'agent.version',
|
||||
originalValue: '8.0.0',
|
||||
values: ['8.0.0'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
field: 'cloud.availability_zone',
|
||||
originalValue: 'projects/189716325846/zones/us-east1-b',
|
||||
values: ['projects/189716325846/zones/us-east1-b'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
field: 'cloud.instance.id',
|
||||
originalValue: '5412578377715150143',
|
||||
values: ['5412578377715150143'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
];
|
||||
|
|
|
@ -2287,11 +2287,13 @@ export const mockTimelineDetails: TimelineEventsDetailsItem[] = [
|
|||
field: 'host.name',
|
||||
values: ['apache'],
|
||||
originalValue: 'apache',
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
field: 'user.id',
|
||||
values: ['1'],
|
||||
originalValue: 1,
|
||||
isObjectArray: false,
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ describe('helpers', () => {
|
|||
{
|
||||
field: 'x',
|
||||
values: ['The nickname of the developer we all :heart:'],
|
||||
isObjectArray: false,
|
||||
originalValue: 'The nickname of the developer we all :heart:',
|
||||
},
|
||||
]);
|
||||
|
@ -40,6 +41,7 @@ describe('helpers', () => {
|
|||
{
|
||||
field: 'x',
|
||||
values: ['The nickname of the developer we all :heart:'],
|
||||
isObjectArray: false,
|
||||
originalValue: 'The nickname of the developer we all :heart:',
|
||||
},
|
||||
]);
|
||||
|
@ -51,6 +53,7 @@ describe('helpers', () => {
|
|||
{
|
||||
field: 'x',
|
||||
values: ['The nickname of the developer we all :heart:', 'We are all made of stars'],
|
||||
isObjectArray: false,
|
||||
originalValue: 'The nickname of the developer we all :heart:',
|
||||
},
|
||||
]);
|
||||
|
@ -65,6 +68,7 @@ describe('helpers', () => {
|
|||
{
|
||||
field: 'x.y.z',
|
||||
values: ['zed'],
|
||||
isObjectArray: false,
|
||||
originalValue: 'zed',
|
||||
},
|
||||
]);
|
||||
|
@ -76,6 +80,7 @@ describe('helpers', () => {
|
|||
{
|
||||
field: 'x.y.z',
|
||||
values: ['zed'],
|
||||
isObjectArray: false,
|
||||
originalValue: 'zed',
|
||||
},
|
||||
]);
|
||||
|
@ -90,6 +95,7 @@ describe('helpers', () => {
|
|||
{
|
||||
field: 'a',
|
||||
values: (5 as unknown) as string[],
|
||||
isObjectArray: false,
|
||||
originalValue: 'zed',
|
||||
},
|
||||
],
|
||||
|
@ -104,7 +110,7 @@ describe('helpers', () => {
|
|||
'when trying to access field:',
|
||||
'a',
|
||||
'from data object of:',
|
||||
[{ field: 'a', originalValue: 'zed', values: 5 }]
|
||||
[{ field: 'a', isObjectArray: false, originalValue: 'zed', values: 5 }]
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -116,6 +122,7 @@ describe('helpers', () => {
|
|||
{
|
||||
field: 'a',
|
||||
values: (['hi', 5] as unknown) as string[],
|
||||
isObjectArray: false,
|
||||
originalValue: 'zed',
|
||||
},
|
||||
],
|
||||
|
@ -130,7 +137,7 @@ describe('helpers', () => {
|
|||
'when trying to access field:',
|
||||
'a',
|
||||
'from data object of:',
|
||||
[{ field: 'a', originalValue: 'zed', values: ['hi', 5] }]
|
||||
[{ field: 'a', isObjectArray: false, originalValue: 'zed', values: ['hi', 5] }]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -86,6 +86,7 @@ export const HeaderComponent: React.FC<Props> = ({
|
|||
getManageTimelineById,
|
||||
timelineId,
|
||||
]);
|
||||
const showSortingCapability = !isEqlOn && !(header.subType && header.subType.nested);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -94,7 +95,7 @@ export const HeaderComponent: React.FC<Props> = ({
|
|||
isLoading={isLoading}
|
||||
isResizing={false}
|
||||
onClick={onColumnSort}
|
||||
showSortingCapability={!isEqlOn}
|
||||
showSortingCapability={showSortingCapability}
|
||||
sort={sort}
|
||||
>
|
||||
<Actions
|
||||
|
|
|
@ -69,7 +69,7 @@ exports[`Columns it renders the expected columns 1`] = `
|
|||
fieldFormat=""
|
||||
fieldName="event.category"
|
||||
fieldType=""
|
||||
key="plain-column-renderer-formatted-field-value-test-event.category-1-event.category-Access"
|
||||
key="plain-column-renderer-formatted-field-value-test-event.category-1-event.category-Access-0"
|
||||
truncate={true}
|
||||
value="Access"
|
||||
/>
|
||||
|
@ -99,7 +99,7 @@ exports[`Columns it renders the expected columns 1`] = `
|
|||
fieldFormat=""
|
||||
fieldName="event.action"
|
||||
fieldType=""
|
||||
key="plain-column-renderer-formatted-field-value-test-event.action-1-event.action-Action"
|
||||
key="plain-column-renderer-formatted-field-value-test-event.action-1-event.action-Action-0"
|
||||
truncate={true}
|
||||
value="Action"
|
||||
/>
|
||||
|
@ -129,7 +129,7 @@ exports[`Columns it renders the expected columns 1`] = `
|
|||
fieldFormat=""
|
||||
fieldName="host.name"
|
||||
fieldType=""
|
||||
key="plain-column-renderer-formatted-field-value-test-host.name-1-host.name-apache"
|
||||
key="plain-column-renderer-formatted-field-value-test-host.name-1-host.name-apache-0"
|
||||
truncate={true}
|
||||
value="apache"
|
||||
/>
|
||||
|
@ -159,7 +159,7 @@ exports[`Columns it renders the expected columns 1`] = `
|
|||
fieldFormat=""
|
||||
fieldName="source.ip"
|
||||
fieldType=""
|
||||
key="plain-column-renderer-formatted-field-value-test-source.ip-1-source.ip-192.168.0.1"
|
||||
key="plain-column-renderer-formatted-field-value-test-source.ip-1-source.ip-192.168.0.1-0"
|
||||
truncate={true}
|
||||
value="192.168.0.1"
|
||||
/>
|
||||
|
@ -189,7 +189,7 @@ exports[`Columns it renders the expected columns 1`] = `
|
|||
fieldFormat=""
|
||||
fieldName="destination.ip"
|
||||
fieldType=""
|
||||
key="plain-column-renderer-formatted-field-value-test-destination.ip-1-destination.ip-192.168.0.3"
|
||||
key="plain-column-renderer-formatted-field-value-test-destination.ip-1-destination.ip-192.168.0.3-0"
|
||||
truncate={true}
|
||||
value="192.168.0.3"
|
||||
/>
|
||||
|
@ -219,7 +219,7 @@ exports[`Columns it renders the expected columns 1`] = `
|
|||
fieldFormat=""
|
||||
fieldName="user.name"
|
||||
fieldType=""
|
||||
key="plain-column-renderer-formatted-field-value-test-user.name-1-user.name-john.dee"
|
||||
key="plain-column-renderer-formatted-field-value-test-user.name-1-user.name-john.dee-0"
|
||||
truncate={true}
|
||||
value="john.dee"
|
||||
/>
|
||||
|
|
|
@ -8,7 +8,7 @@ exports[`get_column_renderer renders correctly against snapshot 1`] = `
|
|||
fieldFormat=""
|
||||
fieldName="event.severity"
|
||||
fieldType=""
|
||||
key="plain-column-renderer-formatted-field-value-test-event.severity-1-message-3"
|
||||
key="plain-column-renderer-formatted-field-value-test-event.severity-1-message-3-0"
|
||||
value="3"
|
||||
/>
|
||||
</span>
|
||||
|
|
|
@ -8,7 +8,7 @@ exports[`plain_column_renderer rendering renders correctly against snapshot 1`]
|
|||
fieldFormat=""
|
||||
fieldName="event.category"
|
||||
fieldType="keyword"
|
||||
key="plain-column-renderer-formatted-field-value-test-event.category-1-event.category-Access"
|
||||
key="plain-column-renderer-formatted-field-value-test-event.category-1-event.category-Access-0"
|
||||
value="Access"
|
||||
/>
|
||||
</span>
|
||||
|
|
|
@ -41,14 +41,27 @@ const columnNamesNotDraggable = [MESSAGE_FIELD_NAME];
|
|||
const FormattedFieldValueComponent: React.FC<{
|
||||
contextId: string;
|
||||
eventId: string;
|
||||
isObjectArray?: boolean;
|
||||
fieldFormat?: string;
|
||||
fieldName: string;
|
||||
fieldType: string;
|
||||
truncate?: boolean;
|
||||
value: string | number | undefined | null;
|
||||
linkValue?: string | null | undefined;
|
||||
}> = ({ contextId, eventId, fieldFormat, fieldName, fieldType, truncate, value, linkValue }) => {
|
||||
if (fieldType === IP_FIELD_TYPE) {
|
||||
}> = ({
|
||||
contextId,
|
||||
eventId,
|
||||
fieldFormat,
|
||||
fieldName,
|
||||
fieldType,
|
||||
isObjectArray = false,
|
||||
truncate,
|
||||
value,
|
||||
linkValue,
|
||||
}) => {
|
||||
if (isObjectArray) {
|
||||
return <>{value}</>;
|
||||
} else if (fieldType === IP_FIELD_TYPE) {
|
||||
return (
|
||||
<FormattedIp
|
||||
eventId={eventId}
|
||||
|
|
|
@ -40,9 +40,9 @@ export const plainColumnRenderer: ColumnRenderer = {
|
|||
linkValues?: string[] | null | undefined;
|
||||
}) =>
|
||||
values != null
|
||||
? values.map((value) => (
|
||||
? values.map((value, i) => (
|
||||
<FormattedFieldValue
|
||||
key={`plain-column-renderer-formatted-field-value-${timelineId}-${columnName}-${eventId}-${field.id}-${value}`}
|
||||
key={`plain-column-renderer-formatted-field-value-${timelineId}-${columnName}-${eventId}-${field.id}-${value}-${i}`}
|
||||
contextId={`plain-column-renderer-formatted-field-value-${timelineId}`}
|
||||
eventId={eventId}
|
||||
fieldFormat={field.format || ''}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { Filter } from '../../../../../../../src/plugins/data/public';
|
||||
import { Filter, IFieldSubType } from '../../../../../../../src/plugins/data/public';
|
||||
|
||||
import { DataProvider } from '../../components/timeline/data_providers/data_provider';
|
||||
import { Sort } from '../../components/timeline/body/sort';
|
||||
|
@ -43,6 +43,7 @@ export interface ColumnHeaderOptions {
|
|||
label?: string;
|
||||
linkField?: string;
|
||||
placeholder?: string;
|
||||
subType?: IFieldSubType;
|
||||
type?: string;
|
||||
width: number;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
export const toArray = <T = string>(value: T | T[] | null): T[] =>
|
||||
Array.isArray(value) ? value : value == null ? [] : [value];
|
||||
|
||||
export const toStringArray = <T = string>(value: T | T[] | null): string[] => {
|
||||
if (Array.isArray(value)) {
|
||||
return value.reduce<string[]>((acc, v) => {
|
||||
|
@ -42,3 +41,47 @@ export const toStringArray = <T = string>(value: T | T[] | null): string[] => {
|
|||
return [`${value}`];
|
||||
}
|
||||
};
|
||||
export const toObjectArrayOfStrings = <T = string>(
|
||||
value: T | T[] | null
|
||||
): Array<{
|
||||
str: string;
|
||||
isObjectArray?: boolean;
|
||||
}> => {
|
||||
if (Array.isArray(value)) {
|
||||
return value.reduce<
|
||||
Array<{
|
||||
str: string;
|
||||
isObjectArray?: boolean;
|
||||
}>
|
||||
>((acc, v) => {
|
||||
if (v != null) {
|
||||
switch (typeof v) {
|
||||
case 'number':
|
||||
case 'boolean':
|
||||
return [...acc, { str: v.toString() }];
|
||||
case 'object':
|
||||
try {
|
||||
return [...acc, { str: JSON.stringify(v), isObjectArray: true }]; // need to track when string is not a simple value
|
||||
} catch {
|
||||
return [...acc, { str: 'Invalid Object' }];
|
||||
}
|
||||
case 'string':
|
||||
return [...acc, { str: v }];
|
||||
default:
|
||||
return [...acc, { str: `${v}` }];
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
} else if (value == null) {
|
||||
return [];
|
||||
} else if (!Array.isArray(value) && typeof value === 'object') {
|
||||
try {
|
||||
return [{ str: JSON.stringify(value), isObjectArray: true }];
|
||||
} catch {
|
||||
return [{ str: 'Invalid Object' }];
|
||||
}
|
||||
} else {
|
||||
return [{ str: `${value}` }];
|
||||
}
|
||||
};
|
||||
|
|
|
@ -11,7 +11,7 @@ import { hostFieldsMap } from '../../../../../../common/ecs/ecs_fields';
|
|||
import { HostsEdges } from '../../../../../../common/search_strategy/security_solution/hosts';
|
||||
|
||||
import { HostAggEsItem, HostBuckets, HostValue } from '../../../../../lib/hosts/types';
|
||||
import { toStringArray } from '../../../../helpers/to_array';
|
||||
import { toObjectArrayOfStrings } from '../../../../helpers/to_array';
|
||||
|
||||
export const HOSTS_FIELDS: readonly string[] = [
|
||||
'_id',
|
||||
|
@ -33,7 +33,11 @@ export const formatHostEdgesData = (
|
|||
flattenedFields.cursor.value = hostId || '';
|
||||
const fieldValue = getHostFieldValue(fieldName, bucket);
|
||||
if (fieldValue != null) {
|
||||
return set(`node.${fieldName}`, toStringArray(fieldValue), flattenedFields);
|
||||
return set(
|
||||
`node.${fieldName}`,
|
||||
toObjectArrayOfStrings(fieldValue).map(({ str }) => str),
|
||||
flattenedFields
|
||||
);
|
||||
}
|
||||
return flattenedFields;
|
||||
},
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { get, getOr, isEmpty } from 'lodash/fp';
|
||||
import { set } from '@elastic/safer-lodash-set/fp';
|
||||
import { mergeFieldsWithHit } from '../../../../../utils/build_query';
|
||||
import { toStringArray } from '../../../../helpers/to_array';
|
||||
import { toObjectArrayOfStrings } from '../../../../helpers/to_array';
|
||||
import {
|
||||
AuthenticationsEdges,
|
||||
AuthenticationHit,
|
||||
|
@ -55,7 +55,11 @@ export const formatAuthenticationData = (
|
|||
const fieldPath = `node.${fieldName}`;
|
||||
const fieldValue = get(fieldPath, mergedResult);
|
||||
if (!isEmpty(fieldValue)) {
|
||||
return set(fieldPath, toStringArray(fieldValue), mergedResult);
|
||||
return set(
|
||||
fieldPath,
|
||||
toObjectArrayOfStrings(fieldValue).map(({ str }) => str),
|
||||
mergedResult
|
||||
);
|
||||
} else {
|
||||
return mergedResult;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import { set } from '@elastic/safer-lodash-set/fp';
|
|||
import { get, has, head } from 'lodash/fp';
|
||||
import { hostFieldsMap } from '../../../../../../common/ecs/ecs_fields';
|
||||
import { HostItem } from '../../../../../../common/search_strategy/security_solution/hosts';
|
||||
import { toStringArray } from '../../../../helpers/to_array';
|
||||
import { toObjectArrayOfStrings } from '../../../../helpers/to_array';
|
||||
|
||||
import { HostAggEsItem, HostBuckets, HostValue } from '../../../../../lib/hosts/types';
|
||||
|
||||
|
@ -42,7 +42,11 @@ export const formatHostItem = (bucket: HostAggEsItem): HostItem =>
|
|||
if (fieldName === '_id') {
|
||||
return set('_id', fieldValue, flattenedFields);
|
||||
}
|
||||
return set(fieldName, toStringArray(fieldValue), flattenedFields);
|
||||
return set(
|
||||
fieldName,
|
||||
toObjectArrayOfStrings(fieldValue).map(({ str }) => str),
|
||||
flattenedFields
|
||||
);
|
||||
}
|
||||
return flattenedFields;
|
||||
}, {});
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
HostsUncommonProcessesEdges,
|
||||
HostsUncommonProcessHit,
|
||||
} from '../../../../../../common/search_strategy/security_solution/hosts/uncommon_processes';
|
||||
import { toStringArray } from '../../../../helpers/to_array';
|
||||
import { toObjectArrayOfStrings } from '../../../../helpers/to_array';
|
||||
import { HostHits } from '../../../../../../common/search_strategy';
|
||||
|
||||
export const uncommonProcessesFields = [
|
||||
|
@ -82,7 +82,11 @@ export const formatUncommonProcessesData = (
|
|||
fieldPath = `node.hosts.0.name`;
|
||||
fieldValue = get(fieldPath, mergedResult);
|
||||
}
|
||||
return set(fieldPath, toStringArray(fieldValue), mergedResult);
|
||||
return set(
|
||||
fieldPath,
|
||||
toObjectArrayOfStrings(fieldValue).map(({ str }) => str),
|
||||
mergedResult
|
||||
);
|
||||
},
|
||||
{
|
||||
node: {
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
NetworkDetailsHostHit,
|
||||
NetworkHit,
|
||||
} from '../../../../../../common/search_strategy/security_solution/network';
|
||||
import { toStringArray } from '../../../../helpers/to_array';
|
||||
import { toObjectArrayOfStrings } from '../../../../helpers/to_array';
|
||||
|
||||
export const getNetworkDetailsAgg = (type: string, networkHit: NetworkHit | {}) => {
|
||||
const firstSeen = getOr(null, `firstSeen.value_as_string`, networkHit);
|
||||
|
@ -53,7 +53,7 @@ const formatHostEcs = (data: Record<string, unknown> | null): HostEcs | null =>
|
|||
}
|
||||
return {
|
||||
...acc,
|
||||
[key]: toStringArray(value),
|
||||
[key]: toObjectArrayOfStrings(value).map(({ str }) => str),
|
||||
};
|
||||
}, {});
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { EqlSearchStrategyResponse } from '../../../../../data_enhanced/common';
|
||||
import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../../common/constants';
|
||||
import { EqlSearchResponse } from '../../../../common/detection_engine/types';
|
||||
import { EqlSearchResponse, EqlSequence } from '../../../../common/detection_engine/types';
|
||||
import { EventHit, TimelineEdges } from '../../../../common/search_strategy';
|
||||
import {
|
||||
TimelineEqlRequestOptions,
|
||||
|
@ -56,51 +56,53 @@ export const buildEqlDsl = (options: TimelineEqlRequestOptions): Record<string,
|
|||
},
|
||||
};
|
||||
};
|
||||
const parseSequences = async (sequences: Array<EqlSequence<unknown>>, fieldRequested: string[]) =>
|
||||
sequences.reduce<Promise<TimelineEdges[]>>(async (acc, sequence, sequenceIndex) => {
|
||||
const sequenceParentId = sequence.events[0]?._id ?? null;
|
||||
const data = await acc;
|
||||
const allData = await Promise.all(
|
||||
sequence.events.map(async (event, eventIndex) => {
|
||||
const item = await formatTimelineData(
|
||||
fieldRequested,
|
||||
TIMELINE_EVENTS_FIELDS,
|
||||
event as EventHit
|
||||
);
|
||||
return Promise.resolve({
|
||||
...item,
|
||||
node: {
|
||||
...item.node,
|
||||
ecs: {
|
||||
...item.node.ecs,
|
||||
...(sequenceParentId != null
|
||||
? {
|
||||
eql: {
|
||||
parentId: sequenceParentId,
|
||||
sequenceNumber: `${sequenceIndex}-${eventIndex}`,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
},
|
||||
});
|
||||
})
|
||||
);
|
||||
return Promise.resolve([...data, ...allData]);
|
||||
}, Promise.resolve([]));
|
||||
|
||||
export const parseEqlResponse = (
|
||||
export const parseEqlResponse = async (
|
||||
options: TimelineEqlRequestOptions,
|
||||
response: EqlSearchStrategyResponse<EqlSearchResponse<unknown>>
|
||||
): Promise<TimelineEqlResponse> => {
|
||||
const { activePage, querySize } = options.pagination;
|
||||
// const totalCount = response.rawResponse?.body?.hits?.total?.value ?? 0;
|
||||
let edges: TimelineEdges[] = [];
|
||||
|
||||
if (response.rawResponse.body.hits.sequences !== undefined) {
|
||||
edges = response.rawResponse.body.hits.sequences.reduce<TimelineEdges[]>(
|
||||
(data, sequence, sequenceIndex) => {
|
||||
const sequenceParentId = sequence.events[0]?._id ?? null;
|
||||
return [
|
||||
...data,
|
||||
...sequence.events.map((event, eventIndex) => {
|
||||
const item = formatTimelineData(
|
||||
options.fieldRequested,
|
||||
TIMELINE_EVENTS_FIELDS,
|
||||
event as EventHit
|
||||
);
|
||||
return {
|
||||
...item,
|
||||
node: {
|
||||
...item.node,
|
||||
ecs: {
|
||||
...item.node.ecs,
|
||||
...(sequenceParentId != null
|
||||
? {
|
||||
eql: {
|
||||
parentId: sequenceParentId,
|
||||
sequenceNumber: `${sequenceIndex}-${eventIndex}`,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
},
|
||||
};
|
||||
}),
|
||||
];
|
||||
},
|
||||
[]
|
||||
);
|
||||
edges = await parseSequences(response.rawResponse.body.hits.sequences, options.fieldRequested);
|
||||
} else if (response.rawResponse.body.hits.events !== undefined) {
|
||||
edges = response.rawResponse.body.hits.events.map((event) =>
|
||||
formatTimelineData(options.fieldRequested, TIMELINE_EVENTS_FIELDS, event as EventHit)
|
||||
edges = await Promise.all(
|
||||
response.rawResponse.body.hits.events.map(async (event) =>
|
||||
formatTimelineData(options.fieldRequested, TIMELINE_EVENTS_FIELDS, event as EventHit)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ export const securitySolutionTimelineEqlSearchStrategyProvider = (
|
|||
},
|
||||
};
|
||||
}),
|
||||
mergeMap((esSearchRes) =>
|
||||
mergeMap(async (esSearchRes) =>
|
||||
parseEqlResponse(
|
||||
request,
|
||||
(esSearchRes as unknown) as EqlSearchStrategyResponse<EqlSearchResponse<unknown>>
|
||||
|
|
|
@ -8,54 +8,23 @@
|
|||
import { EventHit } from '../../../../../../common/search_strategy';
|
||||
import { TIMELINE_EVENTS_FIELDS } from './constants';
|
||||
import { formatTimelineData } from './helpers';
|
||||
import { eventHit } from '../mocks';
|
||||
|
||||
describe('#formatTimelineData', () => {
|
||||
it('happy path', () => {
|
||||
const response: EventHit = {
|
||||
_index: 'auditbeat-7.8.0-2020.11.05-000003',
|
||||
_id: 'tkCt1nUBaEgqnrVSZ8R_',
|
||||
_score: 0,
|
||||
_type: '',
|
||||
fields: {
|
||||
'event.category': ['process'],
|
||||
'process.ppid': [3977],
|
||||
'user.name': ['jenkins'],
|
||||
'process.args': ['go', 'vet', './...'],
|
||||
message: ['Process go (PID: 4313) by user jenkins STARTED'],
|
||||
'process.pid': [4313],
|
||||
'process.working_directory': [
|
||||
'/var/lib/jenkins/workspace/Beats_beats_PR-22624/src/github.com/elastic/beats/libbeat',
|
||||
],
|
||||
'process.entity_id': ['Z59cIkAAIw8ZoK0H'],
|
||||
'host.ip': ['10.224.1.237', 'fe80::4001:aff:fee0:1ed', '172.17.0.1'],
|
||||
'process.name': ['go'],
|
||||
'event.action': ['process_started'],
|
||||
'agent.type': ['auditbeat'],
|
||||
'@timestamp': ['2020-11-17T14:48:08.922Z'],
|
||||
'event.module': ['system'],
|
||||
'event.type': ['start'],
|
||||
'host.name': ['beats-ci-immutable-ubuntu-1804-1605624279743236239'],
|
||||
'process.hash.sha1': ['1eac22336a41e0660fb302add9d97daa2bcc7040'],
|
||||
'host.os.family': ['debian'],
|
||||
'event.kind': ['event'],
|
||||
'host.id': ['e59991e835905c65ed3e455b33e13bd6'],
|
||||
'event.dataset': ['process'],
|
||||
'process.executable': [
|
||||
'/var/lib/jenkins/workspace/Beats_beats_PR-22624/.gvm/versions/go1.14.7.linux.amd64/bin/go',
|
||||
],
|
||||
},
|
||||
_source: {},
|
||||
sort: ['1605624488922', 'beats-ci-immutable-ubuntu-1804-1605624279743236239'],
|
||||
aggregations: {},
|
||||
};
|
||||
|
||||
expect(
|
||||
formatTimelineData(
|
||||
['@timestamp', 'host.name', 'destination.ip', 'source.ip'],
|
||||
TIMELINE_EVENTS_FIELDS,
|
||||
response
|
||||
)
|
||||
).toEqual({
|
||||
it('happy path', async () => {
|
||||
const res = await formatTimelineData(
|
||||
[
|
||||
'@timestamp',
|
||||
'host.name',
|
||||
'destination.ip',
|
||||
'source.ip',
|
||||
'source.geo.location',
|
||||
'threat.indicator.matched.field',
|
||||
],
|
||||
TIMELINE_EVENTS_FIELDS,
|
||||
eventHit
|
||||
);
|
||||
expect(res).toEqual({
|
||||
cursor: {
|
||||
tiebreaker: 'beats-ci-immutable-ubuntu-1804-1605624279743236239',
|
||||
value: '1605624488922',
|
||||
|
@ -72,6 +41,14 @@ describe('#formatTimelineData', () => {
|
|||
field: 'host.name',
|
||||
value: ['beats-ci-immutable-ubuntu-1804-1605624279743236239'],
|
||||
},
|
||||
{
|
||||
field: 'source.geo.location',
|
||||
value: [`{"lon":118.7778,"lat":32.0617}`],
|
||||
},
|
||||
{
|
||||
field: 'threat.indicator.matched.field',
|
||||
value: ['matched_field', 'matched_field_2'],
|
||||
},
|
||||
],
|
||||
ecs: {
|
||||
'@timestamp': ['2020-11-17T14:48:08.922Z'],
|
||||
|
@ -122,7 +99,7 @@ describe('#formatTimelineData', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('rule signal results', () => {
|
||||
it('rule signal results', async () => {
|
||||
const response: EventHit = {
|
||||
_index: '.siem-signals-patrykkopycinski-default-000007',
|
||||
_id: 'a77040f198355793c35bf22b900902371309be615381f0a2ec92c208b6132562',
|
||||
|
@ -290,7 +267,7 @@ describe('#formatTimelineData', () => {
|
|||
};
|
||||
|
||||
expect(
|
||||
formatTimelineData(
|
||||
await formatTimelineData(
|
||||
['@timestamp', 'host.name', 'destination.ip', 'source.ip'],
|
||||
TIMELINE_EVENTS_FIELDS,
|
||||
response
|
||||
|
|
|
@ -6,9 +6,13 @@
|
|||
*/
|
||||
|
||||
import { get, has, merge, uniq } from 'lodash/fp';
|
||||
import { EventHit, TimelineEdges } from '../../../../../../common/search_strategy';
|
||||
import {
|
||||
EventHit,
|
||||
TimelineEdges,
|
||||
TimelineNonEcsData,
|
||||
} from '../../../../../../common/search_strategy';
|
||||
import { toStringArray } from '../../../../helpers/to_array';
|
||||
import { formatGeoLocation, isGeoField } from '../details/helpers';
|
||||
import { getDataSafety, getDataFromFieldsHits } from '../details/helpers';
|
||||
|
||||
const getTimestamp = (hit: EventHit): string => {
|
||||
if (hit.fields && hit.fields['@timestamp']) {
|
||||
|
@ -19,13 +23,14 @@ const getTimestamp = (hit: EventHit): string => {
|
|||
return '';
|
||||
};
|
||||
|
||||
export const formatTimelineData = (
|
||||
export const formatTimelineData = async (
|
||||
dataFields: readonly string[],
|
||||
ecsFields: readonly string[],
|
||||
hit: EventHit
|
||||
) =>
|
||||
uniq([...ecsFields, ...dataFields]).reduce<TimelineEdges>(
|
||||
(flattenedFields, fieldName) => {
|
||||
uniq([...ecsFields, ...dataFields]).reduce<Promise<TimelineEdges>>(
|
||||
async (acc, fieldName) => {
|
||||
const flattenedFields: TimelineEdges = await acc;
|
||||
flattenedFields.node._id = hit._id;
|
||||
flattenedFields.node._index = hit._index;
|
||||
flattenedFields.node.ecs._id = hit._id;
|
||||
|
@ -35,30 +40,81 @@ export const formatTimelineData = (
|
|||
flattenedFields.cursor.value = hit.sort[0];
|
||||
flattenedFields.cursor.tiebreaker = hit.sort[1];
|
||||
}
|
||||
return mergeTimelineFieldsWithHit(fieldName, flattenedFields, hit, dataFields, ecsFields);
|
||||
const waitForIt = await mergeTimelineFieldsWithHit(
|
||||
fieldName,
|
||||
flattenedFields,
|
||||
hit,
|
||||
dataFields,
|
||||
ecsFields
|
||||
);
|
||||
return Promise.resolve(waitForIt);
|
||||
},
|
||||
{
|
||||
Promise.resolve({
|
||||
node: { ecs: { _id: '' }, data: [], _id: '', _index: '' },
|
||||
cursor: {
|
||||
value: '',
|
||||
tiebreaker: null,
|
||||
},
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
const specialFields = ['_id', '_index', '_type', '_score'];
|
||||
|
||||
const mergeTimelineFieldsWithHit = <T>(
|
||||
const getValuesFromFields = async (
|
||||
fieldName: string,
|
||||
hit: EventHit,
|
||||
nestedParentFieldName?: string
|
||||
): Promise<TimelineNonEcsData[]> => {
|
||||
if (specialFields.includes(fieldName)) {
|
||||
return [{ field: fieldName, value: toStringArray(get(fieldName, hit)) }];
|
||||
}
|
||||
|
||||
let fieldToEval;
|
||||
if (has(fieldName, hit._source)) {
|
||||
fieldToEval = {
|
||||
[fieldName]: get(fieldName, hit._source),
|
||||
};
|
||||
} else {
|
||||
if (nestedParentFieldName == null || nestedParentFieldName === fieldName) {
|
||||
fieldToEval = {
|
||||
[fieldName]: hit.fields[fieldName],
|
||||
};
|
||||
} else if (nestedParentFieldName != null) {
|
||||
fieldToEval = {
|
||||
[nestedParentFieldName]: hit.fields[nestedParentFieldName],
|
||||
};
|
||||
} else {
|
||||
// fallback, should never hit
|
||||
fieldToEval = {
|
||||
[fieldName]: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
const formattedData = await getDataSafety(getDataFromFieldsHits, fieldToEval);
|
||||
return formattedData.reduce(
|
||||
(acc: TimelineNonEcsData[], { field, values }) =>
|
||||
// nested fields return all field values, pick only the one we asked for
|
||||
field.includes(fieldName) ? [...acc, { field, value: values }] : acc,
|
||||
[]
|
||||
);
|
||||
};
|
||||
|
||||
const mergeTimelineFieldsWithHit = async <T>(
|
||||
fieldName: string,
|
||||
flattenedFields: T,
|
||||
hit: { _source: {}; fields: Record<string, unknown[]> },
|
||||
hit: EventHit,
|
||||
dataFields: readonly string[],
|
||||
ecsFields: readonly string[]
|
||||
) => {
|
||||
if (fieldName != null || dataFields.includes(fieldName)) {
|
||||
const fieldNameAsArray = fieldName.split('.');
|
||||
const nestedParentFieldName = Object.keys(hit.fields ?? []).find((f) => {
|
||||
return f === fieldNameAsArray.slice(0, f.split('.').length).join('.');
|
||||
});
|
||||
if (
|
||||
has(fieldName, hit._source) ||
|
||||
has(fieldName, hit.fields) ||
|
||||
nestedParentFieldName != null ||
|
||||
specialFields.includes(fieldName)
|
||||
) {
|
||||
const objectWithProperty = {
|
||||
|
@ -67,16 +123,7 @@ const mergeTimelineFieldsWithHit = <T>(
|
|||
data: dataFields.includes(fieldName)
|
||||
? [
|
||||
...get('node.data', flattenedFields),
|
||||
{
|
||||
field: fieldName,
|
||||
value: specialFields.includes(fieldName)
|
||||
? toStringArray(get(fieldName, hit))
|
||||
: isGeoField(fieldName)
|
||||
? formatGeoLocation(hit.fields[fieldName])
|
||||
: has(fieldName, hit._source)
|
||||
? toStringArray(get(fieldName, hit._source))
|
||||
: toStringArray(hit.fields[fieldName]),
|
||||
},
|
||||
...(await getValuesFromFields(fieldName, hit, nestedParentFieldName)),
|
||||
]
|
||||
: get('node.data', flattenedFields),
|
||||
ecs: ecsFields.includes(fieldName)
|
||||
|
|
|
@ -40,8 +40,10 @@ export const timelineEventsAll: SecuritySolutionTimelineFactory<TimelineEventsQu
|
|||
const { activePage, querySize } = options.pagination;
|
||||
const totalCount = response.rawResponse.hits.total || 0;
|
||||
const hits = response.rawResponse.hits.hits;
|
||||
const edges: TimelineEdges[] = hits.map((hit) =>
|
||||
formatTimelineData(options.fieldRequested, TIMELINE_EVENTS_FIELDS, hit as EventHit)
|
||||
const edges: TimelineEdges[] = await Promise.all(
|
||||
hits.map((hit) =>
|
||||
formatTimelineData(options.fieldRequested, TIMELINE_EVENTS_FIELDS, hit as EventHit)
|
||||
)
|
||||
);
|
||||
const inspect = {
|
||||
dsl: [inspectStringifyObject(buildTimelineEventsAllQuery(queryOptions))],
|
||||
|
|
|
@ -5,150 +5,192 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EventHit } from '../../../../../../common/search_strategy';
|
||||
import { getDataFromFieldsHits } from './helpers';
|
||||
import { EventHit, EventSource } from '../../../../../../common/search_strategy';
|
||||
import { getDataFromFieldsHits, getDataFromSourceHits, getDataSafety } from './helpers';
|
||||
import { eventDetailsFormattedFields, eventHit } from '../mocks';
|
||||
|
||||
describe('#getDataFromFieldsHits', () => {
|
||||
it('happy path', () => {
|
||||
const response: EventHit = {
|
||||
_index: 'auditbeat-7.8.0-2020.11.05-000003',
|
||||
_id: 'tkCt1nUBaEgqnrVSZ8R_',
|
||||
_score: 0,
|
||||
_type: '',
|
||||
fields: {
|
||||
'event.category': ['process'],
|
||||
'process.ppid': [3977],
|
||||
'user.name': ['jenkins'],
|
||||
'process.args': ['go', 'vet', './...'],
|
||||
message: ['Process go (PID: 4313) by user jenkins STARTED'],
|
||||
'process.pid': [4313],
|
||||
'process.working_directory': [
|
||||
'/var/lib/jenkins/workspace/Beats_beats_PR-22624/src/github.com/elastic/beats/libbeat',
|
||||
describe('Events Details Helpers', () => {
|
||||
const fields: EventHit['fields'] = eventHit.fields;
|
||||
const resultFields = eventDetailsFormattedFields;
|
||||
describe('#getDataFromFieldsHits', () => {
|
||||
it('happy path', () => {
|
||||
const result = getDataFromFieldsHits(fields);
|
||||
expect(result).toEqual(resultFields);
|
||||
});
|
||||
it('lets get weird', () => {
|
||||
const whackFields = {
|
||||
'crazy.pants': [
|
||||
{
|
||||
'matched.field': ['matched_field'],
|
||||
first_seen: ['2021-02-22T17:29:25.195Z'],
|
||||
provider: ['yourself'],
|
||||
type: ['custom'],
|
||||
'matched.atomic': ['matched_atomic'],
|
||||
lazer: [
|
||||
{
|
||||
'great.field': ['grrrrr'],
|
||||
lazer: [
|
||||
{
|
||||
lazer: [
|
||||
{
|
||||
cool: true,
|
||||
lazer: [
|
||||
{
|
||||
lazer: [
|
||||
{
|
||||
lazer: [
|
||||
{
|
||||
lazer: [
|
||||
{
|
||||
whoa: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
lazer: [
|
||||
{
|
||||
cool: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'great.field': ['grrrrr_2'],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
'process.entity_id': ['Z59cIkAAIw8ZoK0H'],
|
||||
'host.ip': ['10.224.1.237', 'fe80::4001:aff:fee0:1ed', '172.17.0.1'],
|
||||
'process.name': ['go'],
|
||||
'event.action': ['process_started'],
|
||||
'agent.type': ['auditbeat'],
|
||||
'@timestamp': ['2020-11-17T14:48:08.922Z'],
|
||||
'event.module': ['system'],
|
||||
'event.type': ['start'],
|
||||
'host.name': ['beats-ci-immutable-ubuntu-1804-1605624279743236239'],
|
||||
'process.hash.sha1': ['1eac22336a41e0660fb302add9d97daa2bcc7040'],
|
||||
'host.os.family': ['debian'],
|
||||
'event.kind': ['event'],
|
||||
'host.id': ['e59991e835905c65ed3e455b33e13bd6'],
|
||||
'event.dataset': ['process'],
|
||||
'process.executable': [
|
||||
'/var/lib/jenkins/workspace/Beats_beats_PR-22624/.gvm/versions/go1.14.7.linux.amd64/bin/go',
|
||||
],
|
||||
},
|
||||
_source: {},
|
||||
sort: ['1605624488922', 'beats-ci-immutable-ubuntu-1804-1605624279743236239'],
|
||||
aggregations: {},
|
||||
};
|
||||
const whackResultFields = [
|
||||
{
|
||||
category: 'crazy',
|
||||
field: 'crazy.pants.matched.field',
|
||||
values: ['matched_field'],
|
||||
originalValue: ['matched_field'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'crazy',
|
||||
field: 'crazy.pants.first_seen',
|
||||
values: ['2021-02-22T17:29:25.195Z'],
|
||||
originalValue: ['2021-02-22T17:29:25.195Z'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'crazy',
|
||||
field: 'crazy.pants.provider',
|
||||
values: ['yourself'],
|
||||
originalValue: ['yourself'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'crazy',
|
||||
field: 'crazy.pants.type',
|
||||
values: ['custom'],
|
||||
originalValue: ['custom'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'crazy',
|
||||
field: 'crazy.pants.matched.atomic',
|
||||
values: ['matched_atomic'],
|
||||
originalValue: ['matched_atomic'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'crazy',
|
||||
field: 'crazy.pants.lazer.great.field',
|
||||
values: ['grrrrr', 'grrrrr_2'],
|
||||
originalValue: ['grrrrr', 'grrrrr_2'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'crazy',
|
||||
field: 'crazy.pants.lazer.lazer.lazer.cool',
|
||||
values: ['true', 'false'],
|
||||
originalValue: ['true', 'false'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'crazy',
|
||||
field: 'crazy.pants.lazer.lazer.lazer.lazer.lazer.lazer.lazer.whoa',
|
||||
values: ['false'],
|
||||
originalValue: ['false'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
];
|
||||
const result = getDataFromFieldsHits(whackFields);
|
||||
expect(result).toEqual(whackResultFields);
|
||||
});
|
||||
});
|
||||
it('#getDataFromSourceHits', () => {
|
||||
const _source: EventSource = {
|
||||
'@timestamp': '2021-02-24T00:41:06.527Z',
|
||||
'signal.status': 'open',
|
||||
'signal.rule.name': 'Rawr',
|
||||
'threat.indicator': [
|
||||
{
|
||||
provider: 'yourself',
|
||||
type: 'custom',
|
||||
first_seen: ['2021-02-22T17:29:25.195Z'],
|
||||
matched: { atomic: 'atom', field: 'field', type: 'type' },
|
||||
},
|
||||
{
|
||||
provider: 'other_you',
|
||||
type: 'custom',
|
||||
first_seen: '2021-02-22T17:29:25.195Z',
|
||||
matched: { atomic: 'atom', field: 'field', type: 'type' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(getDataFromFieldsHits(response.fields)).toEqual([
|
||||
{
|
||||
category: 'event',
|
||||
field: 'event.category',
|
||||
originalValue: ['process'],
|
||||
values: ['process'],
|
||||
},
|
||||
{ category: 'process', field: 'process.ppid', originalValue: ['3977'], values: ['3977'] },
|
||||
{ category: 'user', field: 'user.name', originalValue: ['jenkins'], values: ['jenkins'] },
|
||||
{
|
||||
category: 'process',
|
||||
field: 'process.args',
|
||||
originalValue: ['go', 'vet', './...'],
|
||||
values: ['go', 'vet', './...'],
|
||||
},
|
||||
{
|
||||
category: 'base',
|
||||
field: 'message',
|
||||
originalValue: ['Process go (PID: 4313) by user jenkins STARTED'],
|
||||
values: ['Process go (PID: 4313) by user jenkins STARTED'],
|
||||
},
|
||||
{ category: 'process', field: 'process.pid', originalValue: ['4313'], values: ['4313'] },
|
||||
{
|
||||
category: 'process',
|
||||
field: 'process.working_directory',
|
||||
originalValue: [
|
||||
'/var/lib/jenkins/workspace/Beats_beats_PR-22624/src/github.com/elastic/beats/libbeat',
|
||||
],
|
||||
values: [
|
||||
'/var/lib/jenkins/workspace/Beats_beats_PR-22624/src/github.com/elastic/beats/libbeat',
|
||||
],
|
||||
},
|
||||
{
|
||||
category: 'process',
|
||||
field: 'process.entity_id',
|
||||
originalValue: ['Z59cIkAAIw8ZoK0H'],
|
||||
values: ['Z59cIkAAIw8ZoK0H'],
|
||||
},
|
||||
{
|
||||
category: 'host',
|
||||
field: 'host.ip',
|
||||
originalValue: ['10.224.1.237', 'fe80::4001:aff:fee0:1ed', '172.17.0.1'],
|
||||
values: ['10.224.1.237', 'fe80::4001:aff:fee0:1ed', '172.17.0.1'],
|
||||
},
|
||||
{ category: 'process', field: 'process.name', originalValue: ['go'], values: ['go'] },
|
||||
{
|
||||
category: 'event',
|
||||
field: 'event.action',
|
||||
originalValue: ['process_started'],
|
||||
values: ['process_started'],
|
||||
},
|
||||
{
|
||||
category: 'agent',
|
||||
field: 'agent.type',
|
||||
originalValue: ['auditbeat'],
|
||||
values: ['auditbeat'],
|
||||
},
|
||||
expect(getDataFromSourceHits(_source)).toEqual([
|
||||
{
|
||||
category: 'base',
|
||||
field: '@timestamp',
|
||||
originalValue: ['2020-11-17T14:48:08.922Z'],
|
||||
values: ['2020-11-17T14:48:08.922Z'],
|
||||
},
|
||||
{ category: 'event', field: 'event.module', originalValue: ['system'], values: ['system'] },
|
||||
{ category: 'event', field: 'event.type', originalValue: ['start'], values: ['start'] },
|
||||
{
|
||||
category: 'host',
|
||||
field: 'host.name',
|
||||
originalValue: ['beats-ci-immutable-ubuntu-1804-1605624279743236239'],
|
||||
values: ['beats-ci-immutable-ubuntu-1804-1605624279743236239'],
|
||||
values: ['2021-02-24T00:41:06.527Z'],
|
||||
originalValue: ['2021-02-24T00:41:06.527Z'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'process',
|
||||
field: 'process.hash.sha1',
|
||||
originalValue: ['1eac22336a41e0660fb302add9d97daa2bcc7040'],
|
||||
values: ['1eac22336a41e0660fb302add9d97daa2bcc7040'],
|
||||
},
|
||||
{ category: 'host', field: 'host.os.family', originalValue: ['debian'], values: ['debian'] },
|
||||
{ category: 'event', field: 'event.kind', originalValue: ['event'], values: ['event'] },
|
||||
{
|
||||
category: 'host',
|
||||
field: 'host.id',
|
||||
originalValue: ['e59991e835905c65ed3e455b33e13bd6'],
|
||||
values: ['e59991e835905c65ed3e455b33e13bd6'],
|
||||
category: 'signal',
|
||||
field: 'signal.status',
|
||||
values: ['open'],
|
||||
originalValue: ['open'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'event',
|
||||
field: 'event.dataset',
|
||||
originalValue: ['process'],
|
||||
values: ['process'],
|
||||
category: 'signal',
|
||||
field: 'signal.rule.name',
|
||||
values: ['Rawr'],
|
||||
originalValue: ['Rawr'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'process',
|
||||
field: 'process.executable',
|
||||
originalValue: [
|
||||
'/var/lib/jenkins/workspace/Beats_beats_PR-22624/.gvm/versions/go1.14.7.linux.amd64/bin/go',
|
||||
],
|
||||
category: 'threat',
|
||||
field: 'threat.indicator',
|
||||
values: [
|
||||
'/var/lib/jenkins/workspace/Beats_beats_PR-22624/.gvm/versions/go1.14.7.linux.amd64/bin/go',
|
||||
'{"provider":"yourself","type":"custom","first_seen":["2021-02-22T17:29:25.195Z"],"matched":{"atomic":"atom","field":"field","type":"type"}}',
|
||||
'{"provider":"other_you","type":"custom","first_seen":"2021-02-22T17:29:25.195Z","matched":{"atomic":"atom","field":"field","type":"type"}}',
|
||||
],
|
||||
originalValue: [
|
||||
'{"provider":"yourself","type":"custom","first_seen":["2021-02-22T17:29:25.195Z"],"matched":{"atomic":"atom","field":"field","type":"type"}}',
|
||||
'{"provider":"other_you","type":"custom","first_seen":"2021-02-22T17:29:25.195Z","matched":{"atomic":"atom","field":"field","type":"type"}}',
|
||||
],
|
||||
isObjectArray: true,
|
||||
},
|
||||
]);
|
||||
});
|
||||
it('#getDataSafety', async () => {
|
||||
const result = await getDataSafety(getDataFromFieldsHits, fields);
|
||||
expect(result).toEqual(resultFields);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,8 +7,12 @@
|
|||
|
||||
import { get, isEmpty, isNumber, isObject, isString } from 'lodash/fp';
|
||||
|
||||
import { EventSource, TimelineEventsDetailsItem } from '../../../../../../common/search_strategy';
|
||||
import { toStringArray } from '../../../../helpers/to_array';
|
||||
import {
|
||||
EventHit,
|
||||
EventSource,
|
||||
TimelineEventsDetailsItem,
|
||||
} from '../../../../../../common/search_strategy';
|
||||
import { toObjectArrayOfStrings, toStringArray } from '../../../../helpers/to_array';
|
||||
|
||||
export const baseCategoryFields = ['@timestamp', 'labels', 'message', 'tags'];
|
||||
|
||||
|
@ -24,7 +28,10 @@ export const formatGeoLocation = (item: unknown[]) => {
|
|||
const itemGeo = item.length > 0 ? (item[0] as { coordinates: number[] }) : null;
|
||||
if (itemGeo != null && !isEmpty(itemGeo.coordinates)) {
|
||||
try {
|
||||
return toStringArray({ long: itemGeo.coordinates[0], lat: itemGeo.coordinates[1] });
|
||||
return toStringArray({
|
||||
lon: itemGeo.coordinates[0],
|
||||
lat: itemGeo.coordinates[1],
|
||||
});
|
||||
} catch {
|
||||
return toStringArray(item);
|
||||
}
|
||||
|
@ -46,13 +53,18 @@ export const getDataFromSourceHits = (
|
|||
const field = path ? `${path}.${source}` : source;
|
||||
const fieldCategory = getFieldCategory(field);
|
||||
|
||||
const objArrStr = toObjectArrayOfStrings(item);
|
||||
const strArr = objArrStr.map(({ str }) => str);
|
||||
const isObjectArray = objArrStr.some((o) => o.isObjectArray);
|
||||
|
||||
return [
|
||||
...accumulator,
|
||||
{
|
||||
category: fieldCategory,
|
||||
field,
|
||||
values: toStringArray(item),
|
||||
originalValue: toStringArray(item),
|
||||
values: strArr,
|
||||
originalValue: strArr,
|
||||
isObjectArray,
|
||||
} as TimelineEventsDetailsItem,
|
||||
];
|
||||
} else if (isObject(item)) {
|
||||
|
@ -65,18 +77,81 @@ export const getDataFromSourceHits = (
|
|||
}, []);
|
||||
|
||||
export const getDataFromFieldsHits = (
|
||||
fields: Record<string, unknown[]>
|
||||
fields: EventHit['fields'],
|
||||
prependField?: string,
|
||||
prependFieldCategory?: string
|
||||
): TimelineEventsDetailsItem[] =>
|
||||
Object.keys(fields).reduce<TimelineEventsDetailsItem[]>((accumulator, field) => {
|
||||
const item: unknown[] = fields[field];
|
||||
const fieldCategory = getFieldCategory(field);
|
||||
return [
|
||||
|
||||
const fieldCategory =
|
||||
prependFieldCategory != null ? prependFieldCategory : getFieldCategory(field);
|
||||
if (isGeoField(field)) {
|
||||
return [
|
||||
...accumulator,
|
||||
{
|
||||
category: fieldCategory,
|
||||
field,
|
||||
values: formatGeoLocation(item),
|
||||
originalValue: formatGeoLocation(item),
|
||||
isObjectArray: true, // important for UI
|
||||
},
|
||||
];
|
||||
}
|
||||
const objArrStr = toObjectArrayOfStrings(item);
|
||||
const strArr = objArrStr.map(({ str }) => str);
|
||||
const isObjectArray = objArrStr.some((o) => o.isObjectArray);
|
||||
const dotField = prependField ? `${prependField}.${field}` : field;
|
||||
|
||||
// return simple field value (non-object, non-array)
|
||||
if (!isObjectArray) {
|
||||
return [
|
||||
...accumulator,
|
||||
{
|
||||
category: fieldCategory,
|
||||
field: dotField,
|
||||
values: strArr,
|
||||
originalValue: strArr,
|
||||
isObjectArray,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// format nested fields
|
||||
const nestedFields = Array.isArray(item)
|
||||
? item
|
||||
.reduce((acc, i) => [...acc, getDataFromFieldsHits(i, dotField, fieldCategory)], [])
|
||||
.flat()
|
||||
: getDataFromFieldsHits(item, prependField, fieldCategory);
|
||||
|
||||
// combine duplicate fields
|
||||
const flat: Record<string, TimelineEventsDetailsItem> = [
|
||||
...accumulator,
|
||||
{
|
||||
category: fieldCategory,
|
||||
field,
|
||||
values: isGeoField(field) ? formatGeoLocation(item) : toStringArray(item),
|
||||
originalValue: toStringArray(item),
|
||||
} as TimelineEventsDetailsItem,
|
||||
];
|
||||
...nestedFields,
|
||||
].reduce(
|
||||
(acc, f) => ({
|
||||
...acc,
|
||||
// acc/flat is hashmap to determine if we already have the field or not without an array iteration
|
||||
// its converted back to array in return with Object.values
|
||||
...(acc[f.field] != null
|
||||
? {
|
||||
[f.field]: {
|
||||
...f,
|
||||
originalValue: acc[f.field].originalValue.includes(f.originalValue[0])
|
||||
? acc[f.field].originalValue
|
||||
: [...acc[f.field].originalValue, ...f.originalValue],
|
||||
values: acc[f.field].values.includes(f.values[0])
|
||||
? acc[f.field].values
|
||||
: [...acc[f.field].values, ...f.values],
|
||||
},
|
||||
}
|
||||
: { [f.field]: f }),
|
||||
}),
|
||||
{}
|
||||
);
|
||||
|
||||
return Object.values(flat);
|
||||
}, []);
|
||||
|
||||
export const getDataSafety = <A, T>(fn: (args: A) => T, args: A): Promise<T> =>
|
||||
new Promise((resolve) => setTimeout(() => resolve(fn(args))));
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { cloneDeep, merge } from 'lodash/fp';
|
||||
import { cloneDeep, merge, unionBy } from 'lodash/fp';
|
||||
|
||||
import { IEsSearchResponse } from '../../../../../../../../../src/plugins/data/common';
|
||||
import {
|
||||
|
@ -13,11 +13,13 @@ import {
|
|||
TimelineEventsQueries,
|
||||
TimelineEventsDetailsStrategyResponse,
|
||||
TimelineEventsDetailsRequestOptions,
|
||||
TimelineEventsDetailsItem,
|
||||
EventSource,
|
||||
} from '../../../../../../common/search_strategy';
|
||||
import { inspectStringifyObject } from '../../../../../utils/build_query';
|
||||
import { SecuritySolutionTimelineFactory } from '../../types';
|
||||
import { buildTimelineDetailsQuery } from './query.events_details.dsl';
|
||||
import { getDataFromSourceHits } from './helpers';
|
||||
import { getDataFromFieldsHits, getDataFromSourceHits, getDataSafety } from './helpers';
|
||||
|
||||
export const timelineEventsDetails: SecuritySolutionTimelineFactory<TimelineEventsQueries.details> = {
|
||||
buildDsl: (options: TimelineEventsDetailsRequestOptions) => {
|
||||
|
@ -29,11 +31,10 @@ export const timelineEventsDetails: SecuritySolutionTimelineFactory<TimelineEven
|
|||
response: IEsSearchResponse<EventHit>
|
||||
): Promise<TimelineEventsDetailsStrategyResponse> => {
|
||||
const { indexName, eventId, docValueFields = [] } = options;
|
||||
const { _source, ...hitsData } = cloneDeep(response.rawResponse.hits.hits[0] ?? {});
|
||||
const { _source, fields, ...hitsData } = cloneDeep(response.rawResponse.hits.hits[0] ?? {});
|
||||
const inspect = {
|
||||
dsl: [inspectStringifyObject(buildTimelineDetailsQuery(indexName, eventId, docValueFields))],
|
||||
};
|
||||
|
||||
if (response.isRunning) {
|
||||
return {
|
||||
...response,
|
||||
|
@ -41,12 +42,19 @@ export const timelineEventsDetails: SecuritySolutionTimelineFactory<TimelineEven
|
|||
inspect,
|
||||
};
|
||||
}
|
||||
const sourceData = await getDataSafety<EventSource, TimelineEventsDetailsItem[]>(
|
||||
getDataFromSourceHits,
|
||||
_source
|
||||
);
|
||||
const fieldsData = await getDataSafety<EventHit['fields'], TimelineEventsDetailsItem[]>(
|
||||
getDataFromFieldsHits,
|
||||
merge(fields, hitsData)
|
||||
);
|
||||
|
||||
const sourceData = getDataFromSourceHits(merge(_source, hitsData));
|
||||
|
||||
const data = unionBy('field', fieldsData, sourceData);
|
||||
return {
|
||||
...response,
|
||||
data: sourceData,
|
||||
data,
|
||||
inspect,
|
||||
};
|
||||
},
|
||||
|
|
|
@ -24,6 +24,7 @@ describe('buildTimelineDetailsQuery', () => {
|
|||
Object {
|
||||
"allowNoIndices": true,
|
||||
"body": Object {
|
||||
"_source": true,
|
||||
"docvalue_fields": Array [
|
||||
Object {
|
||||
"field": "@timestamp",
|
||||
|
@ -38,6 +39,9 @@ describe('buildTimelineDetailsQuery', () => {
|
|||
"field": "agent.name",
|
||||
},
|
||||
],
|
||||
"fields": Array [
|
||||
"*",
|
||||
],
|
||||
"query": Object {
|
||||
"terms": Object {
|
||||
"_id": Array [
|
||||
|
|
|
@ -22,6 +22,8 @@ export const buildTimelineDetailsQuery = (
|
|||
_id: [id],
|
||||
},
|
||||
},
|
||||
fields: ['*'],
|
||||
_source: true,
|
||||
},
|
||||
size: 1,
|
||||
});
|
||||
|
|
|
@ -0,0 +1,329 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export const eventHit = {
|
||||
_index: 'auditbeat-7.8.0-2020.11.05-000003',
|
||||
_id: 'tkCt1nUBaEgqnrVSZ8R_',
|
||||
_score: 0,
|
||||
_type: '',
|
||||
fields: {
|
||||
'event.category': ['process'],
|
||||
'process.ppid': [3977],
|
||||
'user.name': ['jenkins'],
|
||||
'process.args': ['go', 'vet', './...'],
|
||||
message: ['Process go (PID: 4313) by user jenkins STARTED'],
|
||||
'process.pid': [4313],
|
||||
'process.working_directory': [
|
||||
'/var/lib/jenkins/workspace/Beats_beats_PR-22624/src/github.com/elastic/beats/libbeat',
|
||||
],
|
||||
'process.entity_id': ['Z59cIkAAIw8ZoK0H'],
|
||||
'host.ip': ['10.224.1.237', 'fe80::4001:aff:fee0:1ed', '172.17.0.1'],
|
||||
'process.name': ['go'],
|
||||
'event.action': ['process_started'],
|
||||
'agent.type': ['auditbeat'],
|
||||
'@timestamp': ['2020-11-17T14:48:08.922Z'],
|
||||
'event.module': ['system'],
|
||||
'event.type': ['start'],
|
||||
'host.name': ['beats-ci-immutable-ubuntu-1804-1605624279743236239'],
|
||||
'process.hash.sha1': ['1eac22336a41e0660fb302add9d97daa2bcc7040'],
|
||||
'host.os.family': ['debian'],
|
||||
'event.kind': ['event'],
|
||||
'host.id': ['e59991e835905c65ed3e455b33e13bd6'],
|
||||
'event.dataset': ['process'],
|
||||
'process.executable': [
|
||||
'/var/lib/jenkins/workspace/Beats_beats_PR-22624/.gvm/versions/go1.14.7.linux.amd64/bin/go',
|
||||
],
|
||||
'source.geo.location': [{ coordinates: [118.7778, 32.0617], type: 'Point' }],
|
||||
'threat.indicator': [
|
||||
{
|
||||
'matched.field': ['matched_field'],
|
||||
first_seen: ['2021-02-22T17:29:25.195Z'],
|
||||
provider: ['yourself'],
|
||||
type: ['custom'],
|
||||
'matched.atomic': ['matched_atomic'],
|
||||
lazer: [
|
||||
{
|
||||
'great.field': ['grrrrr'],
|
||||
},
|
||||
{
|
||||
'great.field': ['grrrrr_2'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'matched.field': ['matched_field_2'],
|
||||
first_seen: ['2021-02-22T17:29:25.195Z'],
|
||||
provider: ['other_you'],
|
||||
type: ['custom'],
|
||||
'matched.atomic': ['matched_atomic_2'],
|
||||
lazer: [
|
||||
{
|
||||
'great.field': [
|
||||
{
|
||||
wowoe: [
|
||||
{
|
||||
fooooo: ['grrrrr'],
|
||||
},
|
||||
],
|
||||
astring: 'cool',
|
||||
aNumber: 1,
|
||||
anObject: {
|
||||
neat: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
_source: {},
|
||||
sort: ['1605624488922', 'beats-ci-immutable-ubuntu-1804-1605624279743236239'],
|
||||
aggregations: {},
|
||||
};
|
||||
|
||||
export const eventDetailsFormattedFields = [
|
||||
{
|
||||
category: 'event',
|
||||
field: 'event.category',
|
||||
isObjectArray: false,
|
||||
originalValue: ['process'],
|
||||
values: ['process'],
|
||||
},
|
||||
{
|
||||
category: 'process',
|
||||
field: 'process.ppid',
|
||||
isObjectArray: false,
|
||||
originalValue: ['3977'],
|
||||
values: ['3977'],
|
||||
},
|
||||
{
|
||||
category: 'user',
|
||||
field: 'user.name',
|
||||
isObjectArray: false,
|
||||
originalValue: ['jenkins'],
|
||||
values: ['jenkins'],
|
||||
},
|
||||
{
|
||||
category: 'process',
|
||||
field: 'process.args',
|
||||
isObjectArray: false,
|
||||
originalValue: ['go', 'vet', './...'],
|
||||
values: ['go', 'vet', './...'],
|
||||
},
|
||||
{
|
||||
category: 'base',
|
||||
field: 'message',
|
||||
isObjectArray: false,
|
||||
originalValue: ['Process go (PID: 4313) by user jenkins STARTED'],
|
||||
values: ['Process go (PID: 4313) by user jenkins STARTED'],
|
||||
},
|
||||
{
|
||||
category: 'process',
|
||||
field: 'process.pid',
|
||||
isObjectArray: false,
|
||||
originalValue: ['4313'],
|
||||
values: ['4313'],
|
||||
},
|
||||
{
|
||||
category: 'process',
|
||||
field: 'process.working_directory',
|
||||
isObjectArray: false,
|
||||
originalValue: [
|
||||
'/var/lib/jenkins/workspace/Beats_beats_PR-22624/src/github.com/elastic/beats/libbeat',
|
||||
],
|
||||
values: [
|
||||
'/var/lib/jenkins/workspace/Beats_beats_PR-22624/src/github.com/elastic/beats/libbeat',
|
||||
],
|
||||
},
|
||||
{
|
||||
category: 'process',
|
||||
field: 'process.entity_id',
|
||||
isObjectArray: false,
|
||||
originalValue: ['Z59cIkAAIw8ZoK0H'],
|
||||
values: ['Z59cIkAAIw8ZoK0H'],
|
||||
},
|
||||
{
|
||||
category: 'host',
|
||||
field: 'host.ip',
|
||||
isObjectArray: false,
|
||||
originalValue: ['10.224.1.237', 'fe80::4001:aff:fee0:1ed', '172.17.0.1'],
|
||||
values: ['10.224.1.237', 'fe80::4001:aff:fee0:1ed', '172.17.0.1'],
|
||||
},
|
||||
{
|
||||
category: 'process',
|
||||
field: 'process.name',
|
||||
isObjectArray: false,
|
||||
originalValue: ['go'],
|
||||
values: ['go'],
|
||||
},
|
||||
{
|
||||
category: 'event',
|
||||
field: 'event.action',
|
||||
isObjectArray: false,
|
||||
originalValue: ['process_started'],
|
||||
values: ['process_started'],
|
||||
},
|
||||
{
|
||||
category: 'agent',
|
||||
field: 'agent.type',
|
||||
isObjectArray: false,
|
||||
originalValue: ['auditbeat'],
|
||||
values: ['auditbeat'],
|
||||
},
|
||||
{
|
||||
category: 'base',
|
||||
field: '@timestamp',
|
||||
isObjectArray: false,
|
||||
originalValue: ['2020-11-17T14:48:08.922Z'],
|
||||
values: ['2020-11-17T14:48:08.922Z'],
|
||||
},
|
||||
{
|
||||
category: 'event',
|
||||
field: 'event.module',
|
||||
isObjectArray: false,
|
||||
originalValue: ['system'],
|
||||
values: ['system'],
|
||||
},
|
||||
{
|
||||
category: 'event',
|
||||
field: 'event.type',
|
||||
isObjectArray: false,
|
||||
originalValue: ['start'],
|
||||
values: ['start'],
|
||||
},
|
||||
{
|
||||
category: 'host',
|
||||
field: 'host.name',
|
||||
isObjectArray: false,
|
||||
originalValue: ['beats-ci-immutable-ubuntu-1804-1605624279743236239'],
|
||||
values: ['beats-ci-immutable-ubuntu-1804-1605624279743236239'],
|
||||
},
|
||||
{
|
||||
category: 'process',
|
||||
field: 'process.hash.sha1',
|
||||
isObjectArray: false,
|
||||
originalValue: ['1eac22336a41e0660fb302add9d97daa2bcc7040'],
|
||||
values: ['1eac22336a41e0660fb302add9d97daa2bcc7040'],
|
||||
},
|
||||
{
|
||||
category: 'host',
|
||||
field: 'host.os.family',
|
||||
isObjectArray: false,
|
||||
originalValue: ['debian'],
|
||||
values: ['debian'],
|
||||
},
|
||||
{
|
||||
category: 'event',
|
||||
field: 'event.kind',
|
||||
isObjectArray: false,
|
||||
originalValue: ['event'],
|
||||
values: ['event'],
|
||||
},
|
||||
{
|
||||
category: 'host',
|
||||
field: 'host.id',
|
||||
isObjectArray: false,
|
||||
originalValue: ['e59991e835905c65ed3e455b33e13bd6'],
|
||||
values: ['e59991e835905c65ed3e455b33e13bd6'],
|
||||
},
|
||||
{
|
||||
category: 'event',
|
||||
field: 'event.dataset',
|
||||
isObjectArray: false,
|
||||
originalValue: ['process'],
|
||||
values: ['process'],
|
||||
},
|
||||
{
|
||||
category: 'process',
|
||||
field: 'process.executable',
|
||||
isObjectArray: false,
|
||||
originalValue: [
|
||||
'/var/lib/jenkins/workspace/Beats_beats_PR-22624/.gvm/versions/go1.14.7.linux.amd64/bin/go',
|
||||
],
|
||||
values: [
|
||||
'/var/lib/jenkins/workspace/Beats_beats_PR-22624/.gvm/versions/go1.14.7.linux.amd64/bin/go',
|
||||
],
|
||||
},
|
||||
{
|
||||
category: 'source',
|
||||
field: 'source.geo.location',
|
||||
isObjectArray: true,
|
||||
originalValue: [`{"lon":118.7778,"lat":32.0617}`],
|
||||
values: [`{"lon":118.7778,"lat":32.0617}`],
|
||||
},
|
||||
{
|
||||
category: 'threat',
|
||||
field: 'threat.indicator.matched.field',
|
||||
values: ['matched_field', 'matched_field_2'],
|
||||
originalValue: ['matched_field', 'matched_field_2'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'threat',
|
||||
field: 'threat.indicator.first_seen',
|
||||
values: ['2021-02-22T17:29:25.195Z'],
|
||||
originalValue: ['2021-02-22T17:29:25.195Z'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'threat',
|
||||
field: 'threat.indicator.provider',
|
||||
values: ['yourself', 'other_you'],
|
||||
originalValue: ['yourself', 'other_you'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'threat',
|
||||
field: 'threat.indicator.type',
|
||||
values: ['custom'],
|
||||
originalValue: ['custom'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'threat',
|
||||
field: 'threat.indicator.matched.atomic',
|
||||
values: ['matched_atomic', 'matched_atomic_2'],
|
||||
originalValue: ['matched_atomic', 'matched_atomic_2'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'threat',
|
||||
field: 'threat.indicator.lazer.great.field',
|
||||
values: ['grrrrr', 'grrrrr_2'],
|
||||
originalValue: ['grrrrr', 'grrrrr_2'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'threat',
|
||||
field: 'threat.indicator.lazer.great.field.wowoe.fooooo',
|
||||
values: ['grrrrr'],
|
||||
originalValue: ['grrrrr'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'threat',
|
||||
field: 'threat.indicator.lazer.great.field.astring',
|
||||
values: ['cool'],
|
||||
originalValue: ['cool'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'threat',
|
||||
field: 'threat.indicator.lazer.great.field.aNumber',
|
||||
values: ['1'],
|
||||
originalValue: ['1'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'threat',
|
||||
field: 'threat.indicator.lazer.great.field.neat',
|
||||
values: ['true'],
|
||||
originalValue: ['true'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
];
|
|
@ -20,96 +20,133 @@ const EXPECTED_DATA = [
|
|||
field: '@timestamp',
|
||||
values: ['2019-02-10T02:39:44.107Z'],
|
||||
originalValue: ['2019-02-10T02:39:44.107Z'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: '@version',
|
||||
field: '@version',
|
||||
values: ['1'],
|
||||
originalValue: ['1'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: '_id',
|
||||
field: '_id',
|
||||
values: ['QRhG1WgBqd-n62SwZYDT'],
|
||||
originalValue: ['QRhG1WgBqd-n62SwZYDT'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: '_index',
|
||||
field: '_index',
|
||||
values: ['filebeat-7.0.0-iot-2019.06'],
|
||||
originalValue: ['filebeat-7.0.0-iot-2019.06'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: '_score',
|
||||
field: '_score',
|
||||
values: ['1'],
|
||||
originalValue: ['1'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'agent',
|
||||
field: 'agent.ephemeral_id',
|
||||
values: ['909cd6a1-527d-41a5-9585-a7fb5386f851'],
|
||||
originalValue: ['909cd6a1-527d-41a5-9585-a7fb5386f851'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'agent',
|
||||
field: 'agent.hostname',
|
||||
values: ['raspberrypi'],
|
||||
originalValue: ['raspberrypi'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'agent',
|
||||
field: 'agent.id',
|
||||
values: ['4d3ea604-27e5-4ec7-ab64-44f82285d776'],
|
||||
originalValue: ['4d3ea604-27e5-4ec7-ab64-44f82285d776'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'agent',
|
||||
field: 'agent.type',
|
||||
values: ['filebeat'],
|
||||
originalValue: ['filebeat'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'agent',
|
||||
field: 'agent.version',
|
||||
values: ['7.0.0'],
|
||||
originalValue: ['7.0.0'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'destination',
|
||||
field: 'destination.domain',
|
||||
values: ['s3-iad-2.cf.dash.row.aiv-cdn.net'],
|
||||
originalValue: ['s3-iad-2.cf.dash.row.aiv-cdn.net'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'destination',
|
||||
field: 'destination.ip',
|
||||
values: ['10.100.7.196'],
|
||||
originalValue: ['10.100.7.196'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'destination',
|
||||
field: 'destination.port',
|
||||
values: ['40684'],
|
||||
originalValue: ['40684'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'ecs',
|
||||
field: 'ecs.version',
|
||||
values: ['1.0.0-beta2'],
|
||||
originalValue: ['1.0.0-beta2'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'event',
|
||||
field: 'event.dataset',
|
||||
values: ['suricata.eve'],
|
||||
originalValue: ['suricata.eve'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'event',
|
||||
field: 'event.end',
|
||||
values: ['2019-02-10T02:39:44.107Z'],
|
||||
originalValue: ['2019-02-10T02:39:44.107Z'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'event',
|
||||
field: 'event.kind',
|
||||
values: ['event'],
|
||||
originalValue: ['event'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'event',
|
||||
field: 'event.module',
|
||||
values: ['suricata'],
|
||||
originalValue: ['suricata'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'event',
|
||||
field: 'event.type',
|
||||
values: ['fileinfo'],
|
||||
originalValue: ['fileinfo'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'file',
|
||||
|
@ -120,270 +157,484 @@ const EXPECTED_DATA = [
|
|||
originalValue: [
|
||||
'/dm/2$XTMWANo0Q2RZKlH-95UoAahZrOg~/0a9a/bf72/e1da/4c20-919e-0cbabcf7bfe8/75f50c57-d25f-4e97-9e37-01b9f5caa293_audio_13.mp4',
|
||||
],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'file',
|
||||
field: 'file.size',
|
||||
values: ['48277'],
|
||||
originalValue: ['48277'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'fileset',
|
||||
field: 'fileset.name',
|
||||
values: ['eve'],
|
||||
originalValue: ['eve'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'flow',
|
||||
field: 'flow.locality',
|
||||
values: ['public'],
|
||||
originalValue: ['public'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'host',
|
||||
field: 'host.architecture',
|
||||
values: ['armv7l'],
|
||||
originalValue: ['armv7l'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'host',
|
||||
field: 'host.containerized',
|
||||
values: ['false'],
|
||||
originalValue: ['false'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'host',
|
||||
field: 'host.hostname',
|
||||
values: ['raspberrypi'],
|
||||
originalValue: ['raspberrypi'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'host',
|
||||
field: 'host.id',
|
||||
values: ['b19a781f683541a7a25ee345133aa399'],
|
||||
originalValue: ['b19a781f683541a7a25ee345133aa399'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'host',
|
||||
field: 'host.name',
|
||||
values: ['raspberrypi'],
|
||||
originalValue: ['raspberrypi'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'host',
|
||||
field: 'host.os.codename',
|
||||
values: ['stretch'],
|
||||
originalValue: ['stretch'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'host',
|
||||
field: 'host.os.family',
|
||||
values: [''],
|
||||
originalValue: [''],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'host',
|
||||
field: 'host.os.kernel',
|
||||
values: ['4.14.50-v7+'],
|
||||
originalValue: ['4.14.50-v7+'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'host',
|
||||
field: 'host.os.name',
|
||||
values: ['Raspbian GNU/Linux'],
|
||||
originalValue: ['Raspbian GNU/Linux'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'host',
|
||||
field: 'host.os.platform',
|
||||
values: ['raspbian'],
|
||||
originalValue: ['raspbian'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'host',
|
||||
field: 'host.os.version',
|
||||
values: ['9 (stretch)'],
|
||||
originalValue: ['9 (stretch)'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'http',
|
||||
field: 'http.request.method',
|
||||
values: ['get'],
|
||||
originalValue: ['get'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'http',
|
||||
field: 'http.response.body.bytes',
|
||||
values: ['48277'],
|
||||
originalValue: ['48277'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'http',
|
||||
field: 'http.response.status_code',
|
||||
values: ['206'],
|
||||
originalValue: ['206'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'input',
|
||||
field: 'input.type',
|
||||
values: ['log'],
|
||||
originalValue: ['log'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'base',
|
||||
field: 'labels.pipeline',
|
||||
values: ['filebeat-7.0.0-suricata-eve-pipeline'],
|
||||
originalValue: ['filebeat-7.0.0-suricata-eve-pipeline'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'log',
|
||||
field: 'log.file.path',
|
||||
values: ['/var/log/suricata/eve.json'],
|
||||
originalValue: ['/var/log/suricata/eve.json'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'log',
|
||||
field: 'log.offset',
|
||||
values: ['1856288115'],
|
||||
originalValue: ['1856288115'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'network',
|
||||
field: 'network.name',
|
||||
values: ['iot'],
|
||||
originalValue: ['iot'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'network',
|
||||
field: 'network.protocol',
|
||||
values: ['http'],
|
||||
originalValue: ['http'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'network',
|
||||
field: 'network.transport',
|
||||
values: ['tcp'],
|
||||
originalValue: ['tcp'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'service',
|
||||
field: 'service.type',
|
||||
values: ['suricata'],
|
||||
originalValue: ['suricata'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'source',
|
||||
field: 'source.as.num',
|
||||
values: ['16509'],
|
||||
originalValue: ['16509'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'source',
|
||||
field: 'source.as.org',
|
||||
values: ['Amazon.com, Inc.'],
|
||||
originalValue: ['Amazon.com, Inc.'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'source',
|
||||
field: 'source.domain',
|
||||
values: ['server-54-239-219-210.jfk51.r.cloudfront.net'],
|
||||
originalValue: ['server-54-239-219-210.jfk51.r.cloudfront.net'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'source',
|
||||
field: 'source.geo.city_name',
|
||||
values: ['Seattle'],
|
||||
originalValue: ['Seattle'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'source',
|
||||
field: 'source.geo.continent_name',
|
||||
values: ['North America'],
|
||||
originalValue: ['North America'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'source',
|
||||
field: 'source.geo.country_iso_code',
|
||||
values: ['US'],
|
||||
originalValue: ['US'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'source',
|
||||
field: 'source.geo.location',
|
||||
values: ['{"lon":-122.3341,"lat":47.6103}'],
|
||||
originalValue: ['{"lon":-122.3341,"lat":47.6103}'],
|
||||
isObjectArray: true,
|
||||
},
|
||||
{
|
||||
category: 'source',
|
||||
field: 'source.geo.location.lat',
|
||||
values: ['47.6103'],
|
||||
originalValue: ['47.6103'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'source',
|
||||
field: 'source.geo.location.lon',
|
||||
values: ['-122.3341'],
|
||||
originalValue: ['-122.3341'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'source',
|
||||
field: 'source.geo.region_iso_code',
|
||||
values: ['US-WA'],
|
||||
originalValue: ['US-WA'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'source',
|
||||
field: 'source.geo.region_name',
|
||||
values: ['Washington'],
|
||||
originalValue: ['Washington'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'source',
|
||||
field: 'source.ip',
|
||||
values: ['54.239.219.210'],
|
||||
originalValue: ['54.239.219.210'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'source',
|
||||
field: 'source.port',
|
||||
values: ['80'],
|
||||
originalValue: ['80'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'suricata',
|
||||
field: 'suricata.eve.app_proto',
|
||||
values: ['http'],
|
||||
originalValue: ['http'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'suricata',
|
||||
field: 'suricata.eve.dest_ip',
|
||||
values: ['10.100.7.196'],
|
||||
originalValue: ['10.100.7.196'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'suricata',
|
||||
field: 'suricata.eve.dest_port',
|
||||
values: ['40684'],
|
||||
originalValue: ['40684'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'suricata',
|
||||
field: 'suricata.eve.fileinfo.filename',
|
||||
values: [
|
||||
'/dm/2$XTMWANo0Q2RZKlH-95UoAahZrOg~/0a9a/bf72/e1da/4c20-919e-0cbabcf7bfe8/75f50c57-d25f-4e97-9e37-01b9f5caa293_audio_13.mp4',
|
||||
],
|
||||
originalValue: [
|
||||
'/dm/2$XTMWANo0Q2RZKlH-95UoAahZrOg~/0a9a/bf72/e1da/4c20-919e-0cbabcf7bfe8/75f50c57-d25f-4e97-9e37-01b9f5caa293_audio_13.mp4',
|
||||
],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'suricata',
|
||||
field: 'suricata.eve.fileinfo.size',
|
||||
values: ['48277'],
|
||||
originalValue: ['48277'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'suricata',
|
||||
field: 'suricata.eve.fileinfo.state',
|
||||
values: ['CLOSED'],
|
||||
originalValue: ['CLOSED'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'suricata',
|
||||
field: 'suricata.eve.fileinfo.stored',
|
||||
values: ['false'],
|
||||
originalValue: ['false'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'suricata',
|
||||
field: 'suricata.eve.fileinfo.tx_id',
|
||||
values: ['301'],
|
||||
originalValue: ['301'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'suricata',
|
||||
field: 'suricata.eve.flow_id',
|
||||
values: ['196625917175466'],
|
||||
originalValue: ['196625917175466'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'suricata',
|
||||
field: 'suricata.eve.http.hostname',
|
||||
values: ['s3-iad-2.cf.dash.row.aiv-cdn.net'],
|
||||
originalValue: ['s3-iad-2.cf.dash.row.aiv-cdn.net'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'suricata',
|
||||
field: 'suricata.eve.http.http_content_type',
|
||||
values: ['video/mp4'],
|
||||
originalValue: ['video/mp4'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'suricata',
|
||||
field: 'suricata.eve.http.http_method',
|
||||
values: ['get'],
|
||||
originalValue: ['get'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'suricata',
|
||||
field: 'suricata.eve.http.length',
|
||||
values: ['48277'],
|
||||
originalValue: ['48277'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'suricata',
|
||||
field: 'suricata.eve.http.protocol',
|
||||
values: ['HTTP/1.1'],
|
||||
originalValue: ['HTTP/1.1'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'suricata',
|
||||
field: 'suricata.eve.http.status',
|
||||
values: ['206'],
|
||||
originalValue: ['206'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'suricata',
|
||||
field: 'suricata.eve.http.url',
|
||||
values: [
|
||||
'/dm/2$XTMWANo0Q2RZKlH-95UoAahZrOg~/0a9a/bf72/e1da/4c20-919e-0cbabcf7bfe8/75f50c57-d25f-4e97-9e37-01b9f5caa293_audio_13.mp4',
|
||||
],
|
||||
originalValue: [
|
||||
'/dm/2$XTMWANo0Q2RZKlH-95UoAahZrOg~/0a9a/bf72/e1da/4c20-919e-0cbabcf7bfe8/75f50c57-d25f-4e97-9e37-01b9f5caa293_audio_13.mp4',
|
||||
],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'suricata',
|
||||
field: 'suricata.eve.in_iface',
|
||||
values: ['eth0'],
|
||||
originalValue: ['eth0'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'suricata',
|
||||
field: 'suricata.eve.proto',
|
||||
values: ['tcp'],
|
||||
originalValue: ['tcp'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'suricata',
|
||||
field: 'suricata.eve.src_ip',
|
||||
values: ['54.239.219.210'],
|
||||
originalValue: ['54.239.219.210'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'suricata',
|
||||
field: 'suricata.eve.src_port',
|
||||
values: ['80'],
|
||||
originalValue: ['80'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'suricata',
|
||||
field: 'suricata.eve.timestamp',
|
||||
values: ['2019-02-10T02:39:44.107Z'],
|
||||
originalValue: ['2019-02-10T02:39:44.107Z'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'base',
|
||||
field: 'tags',
|
||||
values: ['suricata'],
|
||||
originalValue: ['suricata'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'traefik',
|
||||
field: 'traefik.access.geoip.city_name',
|
||||
values: ['Seattle'],
|
||||
originalValue: ['Seattle'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'traefik',
|
||||
field: 'traefik.access.geoip.continent_name',
|
||||
values: ['North America'],
|
||||
originalValue: ['North America'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'traefik',
|
||||
field: 'traefik.access.geoip.country_iso_code',
|
||||
values: ['US'],
|
||||
originalValue: ['US'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'traefik',
|
||||
field: 'traefik.access.geoip.location',
|
||||
values: ['{"lon":-122.3341,"lat":47.6103}'],
|
||||
originalValue: ['{"lon":-122.3341,"lat":47.6103}'],
|
||||
isObjectArray: true,
|
||||
},
|
||||
{
|
||||
category: 'traefik',
|
||||
field: 'traefik.access.geoip.region_iso_code',
|
||||
values: ['US-WA'],
|
||||
originalValue: ['US-WA'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'traefik',
|
||||
field: 'traefik.access.geoip.region_name',
|
||||
values: ['Washington'],
|
||||
originalValue: ['Washington'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'url',
|
||||
field: 'url.domain',
|
||||
values: ['s3-iad-2.cf.dash.row.aiv-cdn.net'],
|
||||
originalValue: ['s3-iad-2.cf.dash.row.aiv-cdn.net'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'url',
|
||||
|
@ -394,6 +645,7 @@ const EXPECTED_DATA = [
|
|||
originalValue: [
|
||||
'/dm/2$XTMWANo0Q2RZKlH-95UoAahZrOg~/0a9a/bf72/e1da/4c20-919e-0cbabcf7bfe8/75f50c57-d25f-4e97-9e37-01b9f5caa293_audio_13.mp4',
|
||||
],
|
||||
isObjectArray: false,
|
||||
},
|
||||
{
|
||||
category: 'url',
|
||||
|
@ -404,27 +656,9 @@ const EXPECTED_DATA = [
|
|||
originalValue: [
|
||||
'/dm/2$XTMWANo0Q2RZKlH-95UoAahZrOg~/0a9a/bf72/e1da/4c20-919e-0cbabcf7bfe8/75f50c57-d25f-4e97-9e37-01b9f5caa293_audio_13.mp4',
|
||||
],
|
||||
},
|
||||
{
|
||||
category: '_index',
|
||||
field: '_index',
|
||||
values: ['filebeat-7.0.0-iot-2019.06'],
|
||||
originalValue: ['filebeat-7.0.0-iot-2019.06'],
|
||||
},
|
||||
{
|
||||
category: '_id',
|
||||
field: '_id',
|
||||
values: ['QRhG1WgBqd-n62SwZYDT'],
|
||||
originalValue: ['QRhG1WgBqd-n62SwZYDT'],
|
||||
},
|
||||
{
|
||||
category: '_score',
|
||||
field: '_score',
|
||||
values: ['1'],
|
||||
originalValue: ['1'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
];
|
||||
|
||||
const EXPECTED_KPI_COUNTS = {
|
||||
destinationIpCount: 154,
|
||||
hostCount: 1,
|
||||
|
@ -456,7 +690,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
wait_for_completion_timeout: '10s',
|
||||
})
|
||||
.expect(200);
|
||||
expect(sortBy(detailsData, 'name')).to.eql(sortBy(EXPECTED_DATA, 'name'));
|
||||
expect(sortBy(detailsData, 'field')).to.eql(sortBy(EXPECTED_DATA, 'field'));
|
||||
});
|
||||
|
||||
it('Make sure that we get kpi data', async () => {
|
||||
|
|
Loading…
Reference in a new issue