From a6ab21094ab19a548d10d8fcd14c206a05866151 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Wed, 24 Jul 2019 11:41:11 +0200 Subject: [PATCH] [SIEM] Timeline NOT working with unauthorized (#41767) * Allow error to show in the application * Allow unauthorized user to use timeline api with redux storage * add callout to timeline to show + fix event details * Build fixes * fix pinned event * review I * fix details timeline test on api integration --- .../__snapshots__/event_details.test.tsx.snap | 80 -- .../__snapshots__/json_view.test.tsx.snap | 80 -- .../components/event_details/columns.tsx | 9 +- .../event_fields_browser.test.tsx | 8 +- .../event_details/event_fields_browser.tsx | 45 +- .../components/event_details/helpers.test.tsx | 8 +- .../public/components/event_details/types.ts | 10 + .../__snapshots__/timeline.test.tsx.snap | 1 + .../header/__snapshots__/index.test.tsx.snap | 1 + .../components/timeline/header/index.test.tsx | 29 + .../components/timeline/header/index.tsx | 14 + .../timeline/header/translations.ts | 15 + .../siem/public/components/timeline/index.tsx | 9 +- .../components/timeline/timeline.test.tsx | 12 + .../public/components/timeline/timeline.tsx | 3 + .../timeline/details/index.gql_query.ts | 4 - .../siem/public/graphql/introspection.json | 72 +- .../plugins/siem/public/graphql/types.ts | 24 +- .../plugins/siem/public/mock/global_state.ts | 1 + .../siem/public/mock/mock_detail_item.ts | 85 -- .../siem/public/store/timeline/actions.ts | 2 + .../siem/public/store/timeline/epic.ts | 7 + .../public/store/timeline/epic_favorite.ts | 9 +- .../siem/public/store/timeline/epic_note.ts | 8 +- .../store/timeline/epic_pinned_event.ts | 8 +- .../siem/public/store/timeline/helpers.ts | 1 + .../siem/public/store/timeline/reducer.ts | 6 + .../siem/public/store/timeline/selectors.ts | 9 + .../siem/public/store/timeline/types.ts | 1 + .../siem/server/graphql/events/schema.gql.ts | 4 - .../server/graphql/pinned_event/schema.gql.ts | 2 + .../server/graphql/timeline/schema.gql.ts | 2 + .../plugins/siem/server/graphql/types.ts | 72 +- .../lib/events/elasticsearch_adapter.ts | 77 +- .../plugins/siem/server/lib/events/mock.ts | 884 +++++++----------- .../siem/server/lib/note/saved_object.ts | 23 +- .../server/lib/pinned_event/saved_object.ts | 15 +- .../siem/server/lib/timeline/saved_object.ts | 158 ++-- .../apis/siem/timeline_details.ts | 518 ++-------- 39 files changed, 845 insertions(+), 1471 deletions(-) create mode 100644 x-pack/legacy/plugins/siem/public/components/event_details/types.ts create mode 100644 x-pack/legacy/plugins/siem/public/components/timeline/header/translations.ts diff --git a/x-pack/legacy/plugins/siem/public/components/event_details/__snapshots__/event_details.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/event_details/__snapshots__/event_details.test.tsx.snap index f69bc85d5ed4b..399892270f9fb 100644 --- a/x-pack/legacy/plugins/siem/public/components/event_details/__snapshots__/event_details.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/event_details/__snapshots__/event_details.test.tsx.snap @@ -391,221 +391,141 @@ exports[`EventDetails rendering should match snapshot 1`] = ` data={ Array [ Object { - "category": "_id", - "description": "Each document has an _id that uniquely identifies it", - "example": "Y-6TfmcB0WOhS6qyMv3s", "field": "_id", "originalValue": "pEMaMmkBUV60JmNWmWVi", - "type": "keyword", "values": Array [ "pEMaMmkBUV60JmNWmWVi", ], }, Object { - "category": "_index", - "description": "An index is like a ‘database’ in a relational database. It has a mapping which defines multiple types. An index is a logical namespace which maps to one or more primary shards and can have zero or more replica shards.", - "example": "auditbeat-8.0.0-2019.02.19-000001", "field": "_index", "originalValue": "filebeat-8.0.0-2019.02.19-000001", - "type": "keyword", "values": Array [ "filebeat-8.0.0-2019.02.19-000001", ], }, Object { - "category": "_type", - "description": null, - "example": null, "field": "_type", "originalValue": "_doc", - "type": "keyword", "values": Array [ "_doc", ], }, Object { - "category": "_score", - "description": null, - "example": null, "field": "_score", "originalValue": 1, - "type": "long", "values": Array [ "1", ], }, Object { - "category": "@timestamp", - "description": "Date/time when the event originated.For log events this is the date/time when the event was generated, and not when it was read.Required field for all events.", - "example": "2016-05-23T08:05:34.853Z", "field": "@timestamp", "originalValue": "2019-02-28T16:50:54.621Z", - "type": "date", "values": Array [ "2019-02-28T16:50:54.621Z", ], }, Object { - "category": "agent", - "description": "Ephemeral identifier of this agent (if one exists).This id normally changes across restarts, but \`agent.id\` does not.", - "example": "8a4f500f", "field": "agent.ephemeral_id", "originalValue": "9d391ef2-a734-4787-8891-67031178c641", - "type": "keyword", "values": Array [ "9d391ef2-a734-4787-8891-67031178c641", ], }, Object { - "category": "agent", - "description": null, - "example": null, "field": "agent.hostname", "originalValue": "siem-kibana", - "type": "keyword", "values": Array [ "siem-kibana", ], }, Object { - "category": "agent", - "description": "Unique identifier of this agent (if one exists).Example: For Beats this would be beat.id.", - "example": "8a4f500d", "field": "agent.id", "originalValue": "5de03d5f-52f3-482e-91d4-853c7de073c3", - "type": "keyword", "values": Array [ "5de03d5f-52f3-482e-91d4-853c7de073c3", ], }, Object { - "category": "agent", - "description": "Type of the agent.The agent type stays always the same and should be given by the agent used. In case of Filebeat the agent would always be Filebeat also if two Filebeat instances are run on the same machine.", - "example": "filebeat", "field": "agent.type", "originalValue": "filebeat", - "type": "keyword", "values": Array [ "filebeat", ], }, Object { - "category": "agent", - "description": "Version of the agent.", - "example": "6.0.0-rc2", "field": "agent.version", "originalValue": "8.0.0", - "type": "keyword", "values": Array [ "8.0.0", ], }, Object { - "category": "cloud", - "description": "Availability zone in which this host is running.", - "example": "us-east-1c", "field": "cloud.availability_zone", "originalValue": "projects/189716325846/zones/us-east1-b", - "type": "keyword", "values": Array [ "projects/189716325846/zones/us-east1-b", ], }, Object { - "category": "cloud", - "description": "Instance ID of the host machine.", - "example": "i-1234567890abcdef0", "field": "cloud.instance.id", "originalValue": "5412578377715150143", - "type": "keyword", "values": Array [ "5412578377715150143", ], }, Object { - "category": "cloud", - "description": "Instance name of the host machine.", - "example": null, "field": "cloud.instance.name", "originalValue": "siem-kibana", - "type": "keyword", "values": Array [ "siem-kibana", ], }, Object { - "category": "cloud", - "description": "Machine type of the host machine.", - "example": "t2.medium", "field": "cloud.machine.type", "originalValue": "projects/189716325846/machineTypes/n1-standard-1", - "type": "keyword", "values": Array [ "projects/189716325846/machineTypes/n1-standard-1", ], }, Object { - "category": "cloud", - "description": null, - "example": null, "field": "cloud.project.id", "originalValue": "elastic-beats", - "type": "keyword", "values": Array [ "elastic-beats", ], }, Object { - "category": "cloud", - "description": "Name of the cloud provider. Example values are ec2, gce, or digitalocean.", - "example": "ec2", "field": "cloud.provider", "originalValue": "gce", - "type": "keyword", "values": Array [ "gce", ], }, Object { - "category": "destination", - "description": "Bytes sent from the destination to the source.", - "example": "184", "field": "destination.bytes", "originalValue": 584, - "type": "long", "values": Array [ "584", ], }, Object { - "category": "destination", - "description": "IP address of the destination.Can be one or multiple IPv4 or IPv6 addresses.", - "example": null, "field": "destination.ip", "originalValue": "10.47.8.200", - "type": "ip", "values": Array [ "10.47.8.200", ], }, Object { - "category": "destination", - "description": "Packets sent from the destination to the source.", - "example": "12", "field": "destination.packets", "originalValue": 4, - "type": "long", "values": Array [ "4", ], }, Object { - "category": "destination", - "description": "Port of the destination.", - "example": null, "field": "destination.port", "originalValue": 902, - "type": "long", "values": Array [ "902", ], diff --git a/x-pack/legacy/plugins/siem/public/components/event_details/__snapshots__/json_view.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/event_details/__snapshots__/json_view.test.tsx.snap index f26cc9d9000d4..a788b60afd6b3 100644 --- a/x-pack/legacy/plugins/siem/public/components/event_details/__snapshots__/json_view.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/event_details/__snapshots__/json_view.test.tsx.snap @@ -5,221 +5,141 @@ exports[`JSON View rendering should match snapshot 1`] = ` data={ Array [ Object { - "category": "_id", - "description": "Each document has an _id that uniquely identifies it", - "example": "Y-6TfmcB0WOhS6qyMv3s", "field": "_id", "originalValue": "pEMaMmkBUV60JmNWmWVi", - "type": "keyword", "values": Array [ "pEMaMmkBUV60JmNWmWVi", ], }, Object { - "category": "_index", - "description": "An index is like a ‘database’ in a relational database. It has a mapping which defines multiple types. An index is a logical namespace which maps to one or more primary shards and can have zero or more replica shards.", - "example": "auditbeat-8.0.0-2019.02.19-000001", "field": "_index", "originalValue": "filebeat-8.0.0-2019.02.19-000001", - "type": "keyword", "values": Array [ "filebeat-8.0.0-2019.02.19-000001", ], }, Object { - "category": "_type", - "description": null, - "example": null, "field": "_type", "originalValue": "_doc", - "type": "keyword", "values": Array [ "_doc", ], }, Object { - "category": "_score", - "description": null, - "example": null, "field": "_score", "originalValue": 1, - "type": "long", "values": Array [ "1", ], }, Object { - "category": "@timestamp", - "description": "Date/time when the event originated.For log events this is the date/time when the event was generated, and not when it was read.Required field for all events.", - "example": "2016-05-23T08:05:34.853Z", "field": "@timestamp", "originalValue": "2019-02-28T16:50:54.621Z", - "type": "date", "values": Array [ "2019-02-28T16:50:54.621Z", ], }, Object { - "category": "agent", - "description": "Ephemeral identifier of this agent (if one exists).This id normally changes across restarts, but \`agent.id\` does not.", - "example": "8a4f500f", "field": "agent.ephemeral_id", "originalValue": "9d391ef2-a734-4787-8891-67031178c641", - "type": "keyword", "values": Array [ "9d391ef2-a734-4787-8891-67031178c641", ], }, Object { - "category": "agent", - "description": null, - "example": null, "field": "agent.hostname", "originalValue": "siem-kibana", - "type": "keyword", "values": Array [ "siem-kibana", ], }, Object { - "category": "agent", - "description": "Unique identifier of this agent (if one exists).Example: For Beats this would be beat.id.", - "example": "8a4f500d", "field": "agent.id", "originalValue": "5de03d5f-52f3-482e-91d4-853c7de073c3", - "type": "keyword", "values": Array [ "5de03d5f-52f3-482e-91d4-853c7de073c3", ], }, Object { - "category": "agent", - "description": "Type of the agent.The agent type stays always the same and should be given by the agent used. In case of Filebeat the agent would always be Filebeat also if two Filebeat instances are run on the same machine.", - "example": "filebeat", "field": "agent.type", "originalValue": "filebeat", - "type": "keyword", "values": Array [ "filebeat", ], }, Object { - "category": "agent", - "description": "Version of the agent.", - "example": "6.0.0-rc2", "field": "agent.version", "originalValue": "8.0.0", - "type": "keyword", "values": Array [ "8.0.0", ], }, Object { - "category": "cloud", - "description": "Availability zone in which this host is running.", - "example": "us-east-1c", "field": "cloud.availability_zone", "originalValue": "projects/189716325846/zones/us-east1-b", - "type": "keyword", "values": Array [ "projects/189716325846/zones/us-east1-b", ], }, Object { - "category": "cloud", - "description": "Instance ID of the host machine.", - "example": "i-1234567890abcdef0", "field": "cloud.instance.id", "originalValue": "5412578377715150143", - "type": "keyword", "values": Array [ "5412578377715150143", ], }, Object { - "category": "cloud", - "description": "Instance name of the host machine.", - "example": null, "field": "cloud.instance.name", "originalValue": "siem-kibana", - "type": "keyword", "values": Array [ "siem-kibana", ], }, Object { - "category": "cloud", - "description": "Machine type of the host machine.", - "example": "t2.medium", "field": "cloud.machine.type", "originalValue": "projects/189716325846/machineTypes/n1-standard-1", - "type": "keyword", "values": Array [ "projects/189716325846/machineTypes/n1-standard-1", ], }, Object { - "category": "cloud", - "description": null, - "example": null, "field": "cloud.project.id", "originalValue": "elastic-beats", - "type": "keyword", "values": Array [ "elastic-beats", ], }, Object { - "category": "cloud", - "description": "Name of the cloud provider. Example values are ec2, gce, or digitalocean.", - "example": "ec2", "field": "cloud.provider", "originalValue": "gce", - "type": "keyword", "values": Array [ "gce", ], }, Object { - "category": "destination", - "description": "Bytes sent from the destination to the source.", - "example": "184", "field": "destination.bytes", "originalValue": 584, - "type": "long", "values": Array [ "584", ], }, Object { - "category": "destination", - "description": "IP address of the destination.Can be one or multiple IPv4 or IPv6 addresses.", - "example": null, "field": "destination.ip", "originalValue": "10.47.8.200", - "type": "ip", "values": Array [ "10.47.8.200", ], }, Object { - "category": "destination", - "description": "Packets sent from the destination to the source.", - "example": "12", "field": "destination.packets", "originalValue": 4, - "type": "long", "values": Array [ "4", ], }, Object { - "category": "destination", - "description": "Port of the destination.", - "example": null, "field": "destination.port", "originalValue": 902, - "type": "long", "values": Array [ "902", ], diff --git a/x-pack/legacy/plugins/siem/public/components/event_details/columns.tsx b/x-pack/legacy/plugins/siem/public/components/event_details/columns.tsx index a8f8b6bc1365d..4d341e633644b 100644 --- a/x-pack/legacy/plugins/siem/public/components/event_details/columns.tsx +++ b/x-pack/legacy/plugins/siem/public/components/event_details/columns.tsx @@ -12,7 +12,7 @@ import styled from 'styled-components'; import { BrowserFields } from '../../containers/source'; import { DragEffects } from '../drag_and_drop/draggable_wrapper'; import { DefaultDraggable } from '../draggables'; -import { DetailItem, ToStringArray } from '../../graphql/types'; +import { ToStringArray } from '../../graphql/types'; import { DroppableWrapper } from '../drag_and_drop/droppable_wrapper'; import { DraggableFieldBadge } from '../draggables/field_badge'; import { FormattedFieldValue } from '../timeline/body/renderers/formatted_field'; @@ -28,6 +28,7 @@ import * as i18n from './translations'; import { OverflowField } from '../tables/helpers'; import { DATE_FIELD_TYPE, MESSAGE_FIELD_NAME } from '../timeline/body/renderers/constants'; import { EVENT_DURATION_FIELD_NAME } from '../duration'; +import { EventFieldsData } from './types'; const HoverActionsContainer = styled(EuiPanel)` align-items: center; @@ -76,7 +77,7 @@ export const getColumns = ({ name: i18n.FIELD, sortable: true, truncateText: false, - render: (field: string, data: DetailItem) => ( + render: (field: string, data: EventFieldsData) => ( ( + render: (values: ToStringArray | null | undefined, data: EventFieldsData) => ( {values != null && values.map((value, i) => ( @@ -174,7 +175,7 @@ export const getColumns = ({ { field: 'description', name: i18n.DESCRIPTION, - render: (description: string | null | undefined, data: DetailItem) => ( + render: (description: string | null | undefined, data: EventFieldsData) => ( {`${description || ''} ${getExampleText(data.example)}`} ), sortable: true, diff --git a/x-pack/legacy/plugins/siem/public/components/event_details/event_fields_browser.test.tsx b/x-pack/legacy/plugins/siem/public/components/event_details/event_fields_browser.test.tsx index 2a38e0816dd12..f481da69232b2 100644 --- a/x-pack/legacy/plugins/siem/public/components/event_details/event_fields_browser.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/event_details/event_fields_browser.test.tsx @@ -101,7 +101,7 @@ describe('EventFieldsBrowser', () => { .find('[data-test-subj="field-name"]') .at(0) .text() - ).toEqual('_id'); + ).toEqual('@timestamp'); }); }); @@ -124,7 +124,7 @@ describe('EventFieldsBrowser', () => { .find('[data-test-subj="draggable-content"]') .at(0) .text() - ).toEqual('pEMaMmkBUV60JmNWmWVi'); + ).toEqual('Feb 28, 2019 @ 16:50:54.621'); }); }); @@ -149,7 +149,9 @@ describe('EventFieldsBrowser', () => { .find('.euiTableRowCell') .at(3) .text() - ).toContain('Each document has an _id that uniquely identifies it'); + ).toContain( + 'DescriptionDate/time when the event originated. For log events this is the date/time when the event was generated, and not when it was read. Required field for all events. Example: 2016-05-23T08:05:34.853Z' + ); }); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/event_details/event_fields_browser.tsx b/x-pack/legacy/plugins/siem/public/components/event_details/event_fields_browser.tsx index c10cb09d51ca1..e8be940fe831d 100644 --- a/x-pack/legacy/plugins/siem/public/components/event_details/event_fields_browser.tsx +++ b/x-pack/legacy/plugins/siem/public/components/event_details/event_fields_browser.tsx @@ -11,7 +11,8 @@ import { import * as React from 'react'; import { pure } from 'recompose'; -import { BrowserFields } from '../../containers/source'; +import { sortBy } from 'lodash'; +import { BrowserFields, getAllFieldsByName } from '../../containers/source'; import { DetailItem } from '../../graphql/types'; import { OnUpdateColumns } from '../timeline/events'; @@ -29,22 +30,28 @@ interface Props { /** Renders a table view or JSON view of the `ECS` `data` */ export const EventFieldsBrowser = pure( - ({ browserFields, data, eventId, isLoading, onUpdateColumns, timelineId }) => ( - ({ - ...item, - valuesConcatenated: item.values != null ? item.values.join() : '', - }))} - columns={getColumns({ - browserFields, - eventId, - isLoading, - onUpdateColumns, - timelineId, - })} - pagination={false} - search={search} - sorting={true} - /> - ) + ({ browserFields, data, eventId, isLoading, onUpdateColumns, timelineId }) => { + const fieldsByName = getAllFieldsByName(browserFields); + return ( + { + return { + ...item, + ...fieldsByName[item.field], + valuesConcatenated: item.values != null ? item.values.join() : '', + }; + })} + columns={getColumns({ + browserFields, + eventId, + isLoading, + onUpdateColumns, + timelineId, + })} + pagination={false} + search={search} + sorting={true} + /> + ); + } ); diff --git a/x-pack/legacy/plugins/siem/public/components/event_details/helpers.test.tsx b/x-pack/legacy/plugins/siem/public/components/event_details/helpers.test.tsx index 2104a331198dd..e662283f8b000 100644 --- a/x-pack/legacy/plugins/siem/public/components/event_details/helpers.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/event_details/helpers.test.tsx @@ -7,13 +7,17 @@ import { mockDetailItemData } from '../../mock/mock_detail_item'; import { getExampleText, getIconFromType } from './helpers'; +import { mockBrowserFields } from '../../containers/source/mock'; -const aField = mockDetailItemData[0]; +const aField = { + ...mockDetailItemData[4], + ...mockBrowserFields.base.fields!['@timestamp'], +}; describe('helpers', () => { describe('getExampleText', () => { test('it returns the expected example text when the field contains an example', () => { - expect(getExampleText(aField.example)).toEqual('Example: Y-6TfmcB0WOhS6qyMv3s'); + expect(getExampleText(aField.example)).toEqual('Example: 2016-05-23T08:05:34.853Z'); }); test(`it returns an empty string when the field's example is an empty string`, () => { diff --git a/x-pack/legacy/plugins/siem/public/components/event_details/types.ts b/x-pack/legacy/plugins/siem/public/components/event_details/types.ts new file mode 100644 index 0000000000000..4e351fcdf98e4 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/event_details/types.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { BrowserField } from '../../containers/source'; +import { DetailItem } from '../../graphql/types'; + +export type EventFieldsData = BrowserField & DetailItem; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/__snapshots__/timeline.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/timeline/__snapshots__/timeline.test.tsx.snap index 56854111e5e5f..05fb08a6bf6ec 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/__snapshots__/timeline.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/timeline/__snapshots__/timeline.test.tsx.snap @@ -741,6 +741,7 @@ In other use cases the message field can be used to concatenate different values onToggleDataProviderEnabled={[MockFunction]} onToggleDataProviderExcluded={[MockFunction]} show={true} + showCallOutUnauthorizedMsg={false} sort={ Object { "columnId": "@timestamp", diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/header/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/timeline/header/__snapshots__/index.test.tsx.snap index e2ae412ffa8e3..332942c3e3a86 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/header/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/timeline/header/__snapshots__/index.test.tsx.snap @@ -230,6 +230,7 @@ exports[`Header rendering renders correctly against snapshot 1`] = ` onToggleDataProviderEnabled={[MockFunction]} onToggleDataProviderExcluded={[MockFunction]} show={true} + showCallOutUnauthorizedMsg={false} sort={ Object { "columnId": "@timestamp", diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/header/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/header/index.test.tsx index 539954ba7cb4b..baabd2e0f6a82 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/header/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/header/index.test.tsx @@ -33,6 +33,7 @@ describe('Header', () => { onToggleDataProviderEnabled={jest.fn()} onToggleDataProviderExcluded={jest.fn()} show={true} + showCallOutUnauthorizedMsg={false} sort={{ columnId: '@timestamp', sortDirection: Direction.desc, @@ -57,6 +58,7 @@ describe('Header', () => { onToggleDataProviderEnabled={jest.fn()} onToggleDataProviderExcluded={jest.fn()} show={true} + showCallOutUnauthorizedMsg={false} sort={{ columnId: '@timestamp', sortDirection: Direction.desc, @@ -67,5 +69,32 @@ describe('Header', () => { expect(wrapper.find('[data-test-subj="dataProviders"]').exists()).toEqual(true); }); + + test('it renders the unauthorized call out providers', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="timelineCallOutUnauthorized"]').exists()).toEqual(true); + }); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/header/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/header/index.tsx index 15624dec9b7e7..ca870304a7fb8 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/header/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/header/index.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { EuiCallOut } from '@elastic/eui'; import * as React from 'react'; import { pure } from 'recompose'; import styled from 'styled-components'; @@ -23,6 +24,8 @@ import { import { StatefulSearchOrFilter } from '../search_or_filter'; import { BrowserFields } from '../../../containers/source'; +import * as i18n from './translations'; + interface Props { browserFields: BrowserFields; dataProviders: DataProvider[]; @@ -35,6 +38,7 @@ interface Props { onToggleDataProviderEnabled: OnToggleDataProviderEnabled; onToggleDataProviderExcluded: OnToggleDataProviderExcluded; show: boolean; + showCallOutUnauthorizedMsg: boolean; sort: Sort; } @@ -55,8 +59,18 @@ export const TimelineHeader = pure( onToggleDataProviderEnabled, onToggleDataProviderExcluded, show, + showCallOutUnauthorizedMsg, }) => ( + {showCallOutUnauthorizedMsg && ( + + )} { sort, start, show, + showCallOutUnauthorizedMsg, }: Props) => id !== this.props.id || flyoutHeaderHeight !== this.props.flyoutHeaderHeight || @@ -155,7 +157,8 @@ class StatefulTimelineComponent extends React.Component { pageCount !== this.props.pageCount || !isEqual(sort, this.props.sort) || start !== this.props.start || - show !== this.props.show; + show !== this.props.show || + showCallOutUnauthorizedMsg !== this.props.showCallOutUnauthorizedMsg; public componentDidMount() { const { createTimeline, id } = this.props; @@ -179,6 +182,7 @@ class StatefulTimelineComponent extends React.Component { kqlMode, kqlQueryExpression, show, + showCallOutUnauthorizedMsg, start, sort, } = this.props; @@ -208,6 +212,7 @@ class StatefulTimelineComponent extends React.Component { onToggleDataProviderEnabled={this.onToggleDataProviderEnabled} onToggleDataProviderExcluded={this.onToggleDataProviderExcluded} show={show!} + showCallOutUnauthorizedMsg={showCallOutUnauthorizedMsg} start={start} sort={sort!} /> @@ -274,6 +279,7 @@ class StatefulTimelineComponent extends React.Component { } const makeMapStateToProps = () => { + const getShowCallOutUnauthorizedMsg = timelineSelectors.getShowCallOutUnauthorizedMsg(); const getTimeline = timelineSelectors.getTimelineByIdSelector(); const getKqlQueryTimeline = timelineSelectors.getKqlFilterQuerySelector(); const getInputsTimeline = inputsSelectors.getTimelineSelector(); @@ -303,6 +309,7 @@ const makeMapStateToProps = () => { kqlQueryExpression, sort, start: input.timerange.from, + showCallOutUnauthorizedMsg: getShowCallOutUnauthorizedMsg(state), show, }; }; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.test.tsx index c6c1356cac574..3dc223bbda41f 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.test.tsx @@ -67,6 +67,7 @@ describe('Timeline', () => { onToggleDataProviderEnabled={jest.fn()} onToggleDataProviderExcluded={jest.fn()} show={true} + showCallOutUnauthorizedMsg={false} start={startDate} sort={sort} /> @@ -100,6 +101,7 @@ describe('Timeline', () => { onToggleDataProviderEnabled={jest.fn()} onToggleDataProviderExcluded={jest.fn()} show={true} + showCallOutUnauthorizedMsg={false} start={startDate} sort={sort} /> @@ -136,6 +138,7 @@ describe('Timeline', () => { onToggleDataProviderEnabled={jest.fn()} onToggleDataProviderExcluded={jest.fn()} show={true} + showCallOutUnauthorizedMsg={false} start={startDate} sort={sort} /> @@ -172,6 +175,7 @@ describe('Timeline', () => { onToggleDataProviderEnabled={jest.fn()} onToggleDataProviderExcluded={jest.fn()} show={true} + showCallOutUnauthorizedMsg={false} start={startDate} sort={sort} /> @@ -213,6 +217,7 @@ describe('Timeline', () => { onToggleDataProviderEnabled={jest.fn()} onToggleDataProviderExcluded={jest.fn()} show={true} + showCallOutUnauthorizedMsg={false} start={startDate} sort={sort} /> @@ -256,6 +261,7 @@ describe('Timeline', () => { onToggleDataProviderEnabled={jest.fn()} onToggleDataProviderExcluded={jest.fn()} show={true} + showCallOutUnauthorizedMsg={false} start={startDate} sort={sort} /> @@ -307,6 +313,7 @@ describe('Timeline', () => { onToggleDataProviderEnabled={mockOnToggleDataProviderEnabled} onToggleDataProviderExcluded={jest.fn()} show={true} + showCallOutUnauthorizedMsg={false} start={startDate} sort={sort} /> @@ -362,6 +369,7 @@ describe('Timeline', () => { onToggleDataProviderEnabled={jest.fn()} onToggleDataProviderExcluded={mockOnToggleDataProviderExcluded} show={true} + showCallOutUnauthorizedMsg={false} start={startDate} sort={sort} /> @@ -420,6 +428,7 @@ describe('Timeline', () => { onToggleDataProviderEnabled={jest.fn()} onToggleDataProviderExcluded={jest.fn()} show={true} + showCallOutUnauthorizedMsg={false} start={startDate} sort={sort} /> @@ -468,6 +477,7 @@ describe('Timeline', () => { onToggleDataProviderEnabled={jest.fn()} onToggleDataProviderExcluded={jest.fn()} show={true} + showCallOutUnauthorizedMsg={false} start={startDate} sort={sort} /> @@ -522,6 +532,7 @@ describe('Timeline', () => { onToggleDataProviderEnabled={mockOnToggleDataProviderEnabled} onToggleDataProviderExcluded={jest.fn()} show={true} + showCallOutUnauthorizedMsg={false} start={startDate} sort={sort} /> @@ -580,6 +591,7 @@ describe('Timeline', () => { onToggleDataProviderEnabled={jest.fn()} onToggleDataProviderExcluded={mockOnToggleDataProviderExcluded} show={true} + showCallOutUnauthorizedMsg={false} start={startDate} sort={sort} /> diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx index 51fb303376d01..006b84a1af9a3 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx @@ -71,6 +71,7 @@ interface Props { onToggleDataProviderEnabled: OnToggleDataProviderEnabled; onToggleDataProviderExcluded: OnToggleDataProviderExcluded; show: boolean; + showCallOutUnauthorizedMsg: boolean; start: number; sort: Sort; } @@ -99,6 +100,7 @@ export const Timeline = pure( onToggleDataProviderEnabled, onToggleDataProviderExcluded, show, + showCallOutUnauthorizedMsg, start, sort, }) => { @@ -134,6 +136,7 @@ export const Timeline = pure( onToggleDataProviderEnabled={onToggleDataProviderEnabled} onToggleDataProviderExcluded={onToggleDataProviderExcluded} show={show} + showCallOutUnauthorizedMsg={showCallOutUnauthorizedMsg} sort={sort} /> diff --git a/x-pack/legacy/plugins/siem/public/containers/timeline/details/index.gql_query.ts b/x-pack/legacy/plugins/siem/public/containers/timeline/details/index.gql_query.ts index 605f59cedfb65..4677d2328be87 100644 --- a/x-pack/legacy/plugins/siem/public/containers/timeline/details/index.gql_query.ts +++ b/x-pack/legacy/plugins/siem/public/containers/timeline/details/index.gql_query.ts @@ -17,11 +17,7 @@ export const timelineDetailsQuery = gql` id TimelineDetails(eventId: $eventId, indexName: $indexName, defaultIndex: $defaultIndex) { data { - category - description - example field - type values originalValue } diff --git a/x-pack/legacy/plugins/siem/public/graphql/introspection.json b/x-pack/legacy/plugins/siem/public/graphql/introspection.json index d63c489fc9317..fe0187f93d2a5 100644 --- a/x-pack/legacy/plugins/siem/public/graphql/introspection.json +++ b/x-pack/legacy/plugins/siem/public/graphql/introspection.json @@ -527,6 +527,22 @@ "name": "PinnedEvent", "description": "", "fields": [ + { + "name": "code", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "message", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "pinnedEventId", "description": "", @@ -5483,34 +5499,6 @@ "name": "DetailItem", "description": "", "fields": [ - { - "name": "category", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "description", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "example", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, { "name": "field", "description": "", @@ -5523,18 +5511,6 @@ "isDeprecated": false, "deprecationReason": null }, - { - "name": "type", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, { "name": "values", "description": "", @@ -9847,6 +9823,22 @@ "name": "ResponseFavoriteTimeline", "description": "", "fields": [ + { + "name": "code", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "message", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "savedObjectId", "description": "", diff --git a/x-pack/legacy/plugins/siem/public/graphql/types.ts b/x-pack/legacy/plugins/siem/public/graphql/types.ts index 3f4298853df3f..49eb6ef8fd68a 100644 --- a/x-pack/legacy/plugins/siem/public/graphql/types.ts +++ b/x-pack/legacy/plugins/siem/public/graphql/types.ts @@ -79,6 +79,10 @@ export interface ResponseNotes { } export interface PinnedEvent { + code?: number | null; + + message?: string | null; + pinnedEventId: string; eventId?: string | null; @@ -846,16 +850,8 @@ export interface TimelineDetailsData { } export interface DetailItem { - category: string; - - description?: string | null; - - example?: string | null; - field: string; - type: string; - values?: ToStringArray | null; originalValue?: EsValue | null; @@ -1476,6 +1472,10 @@ export interface ResponseTimeline { } export interface ResponseFavoriteTimeline { + code?: number | null; + + message?: string | null; + savedObjectId: string; version: string; @@ -3721,16 +3721,8 @@ export namespace GetTimelineDetailsQuery { export type Data = { __typename?: 'DetailItem'; - category: string; - - description?: string | null; - - example?: string | null; - field: string; - type: string; - values?: ToStringArray | null; originalValue?: EsValue | null; diff --git a/x-pack/legacy/plugins/siem/public/mock/global_state.ts b/x-pack/legacy/plugins/siem/public/mock/global_state.ts index dcdd1f312ede3..02c4a1bf280e1 100644 --- a/x-pack/legacy/plugins/siem/public/mock/global_state.ts +++ b/x-pack/legacy/plugins/siem/public/mock/global_state.ts @@ -120,6 +120,7 @@ export const mockGlobalState: State = { }, dragAndDrop: { dataProviders: {} }, timeline: { + showCallOutUnauthorizedMsg: false, autoSavedWarningMsg: { timelineId: null, newTimelineModel: null, diff --git a/x-pack/legacy/plugins/siem/public/mock/mock_detail_item.ts b/x-pack/legacy/plugins/siem/public/mock/mock_detail_item.ts index c44b44365a3ca..c25428649d563 100644 --- a/x-pack/legacy/plugins/siem/public/mock/mock_detail_item.ts +++ b/x-pack/legacy/plugins/siem/public/mock/mock_detail_item.ts @@ -10,187 +10,102 @@ export const mockDetailItemDataId = 'Y-6TfmcB0WOhS6qyMv3s'; export const mockDetailItemData: DetailItem[] = [ { - category: '_id', - description: 'Each document has an _id that uniquely identifies it', - example: 'Y-6TfmcB0WOhS6qyMv3s', field: '_id', - type: 'keyword', originalValue: 'pEMaMmkBUV60JmNWmWVi', values: ['pEMaMmkBUV60JmNWmWVi'], }, { - category: '_index', - description: - 'An index is like a ‘database’ in a relational database. It has a mapping which defines multiple types. An index is a logical namespace which maps to one or more primary shards and can have zero or more replica shards.', - example: 'auditbeat-8.0.0-2019.02.19-000001', field: '_index', - type: 'keyword', originalValue: 'filebeat-8.0.0-2019.02.19-000001', values: ['filebeat-8.0.0-2019.02.19-000001'], }, { - category: '_type', - description: null, - example: null, field: '_type', - type: 'keyword', originalValue: '_doc', values: ['_doc'], }, { - category: '_score', - description: null, - example: null, field: '_score', - type: 'long', originalValue: 1, values: ['1'], }, { - category: '@timestamp', - description: - 'Date/time when the event originated.For log events this is the date/time when the event was generated, and not when it was read.Required field for all events.', - example: '2016-05-23T08:05:34.853Z', field: '@timestamp', - type: 'date', originalValue: '2019-02-28T16:50:54.621Z', values: ['2019-02-28T16:50:54.621Z'], }, { - category: 'agent', - description: - 'Ephemeral identifier of this agent (if one exists).This id normally changes across restarts, but `agent.id` does not.', - example: '8a4f500f', field: 'agent.ephemeral_id', - type: 'keyword', originalValue: '9d391ef2-a734-4787-8891-67031178c641', values: ['9d391ef2-a734-4787-8891-67031178c641'], }, { - category: 'agent', - description: null, - example: null, field: 'agent.hostname', - type: 'keyword', originalValue: 'siem-kibana', values: ['siem-kibana'], }, { - category: 'agent', - description: - 'Unique identifier of this agent (if one exists).Example: For Beats this would be beat.id.', - example: '8a4f500d', field: 'agent.id', - type: 'keyword', originalValue: '5de03d5f-52f3-482e-91d4-853c7de073c3', values: ['5de03d5f-52f3-482e-91d4-853c7de073c3'], }, { - category: 'agent', - description: - 'Type of the agent.The agent type stays always the same and should be given by the agent used. In case of Filebeat the agent would always be Filebeat also if two Filebeat instances are run on the same machine.', - example: 'filebeat', field: 'agent.type', - type: 'keyword', originalValue: 'filebeat', values: ['filebeat'], }, { - category: 'agent', - description: 'Version of the agent.', - example: '6.0.0-rc2', field: 'agent.version', - type: 'keyword', originalValue: '8.0.0', values: ['8.0.0'], }, { - category: 'cloud', - description: 'Availability zone in which this host is running.', - example: 'us-east-1c', field: 'cloud.availability_zone', - type: 'keyword', originalValue: 'projects/189716325846/zones/us-east1-b', values: ['projects/189716325846/zones/us-east1-b'], }, { - category: 'cloud', - description: 'Instance ID of the host machine.', - example: 'i-1234567890abcdef0', field: 'cloud.instance.id', - type: 'keyword', originalValue: '5412578377715150143', values: ['5412578377715150143'], }, { - category: 'cloud', - description: 'Instance name of the host machine.', - example: null, field: 'cloud.instance.name', - type: 'keyword', originalValue: 'siem-kibana', values: ['siem-kibana'], }, { - category: 'cloud', - description: 'Machine type of the host machine.', - example: 't2.medium', field: 'cloud.machine.type', - type: 'keyword', originalValue: 'projects/189716325846/machineTypes/n1-standard-1', values: ['projects/189716325846/machineTypes/n1-standard-1'], }, { - category: 'cloud', - description: null, - example: null, field: 'cloud.project.id', - type: 'keyword', originalValue: 'elastic-beats', values: ['elastic-beats'], }, { - category: 'cloud', - description: 'Name of the cloud provider. Example values are ec2, gce, or digitalocean.', - example: 'ec2', field: 'cloud.provider', - type: 'keyword', originalValue: 'gce', values: ['gce'], }, { - category: 'destination', - description: 'Bytes sent from the destination to the source.', - example: '184', field: 'destination.bytes', - type: 'long', originalValue: 584, values: ['584'], }, { - category: 'destination', - description: 'IP address of the destination.Can be one or multiple IPv4 or IPv6 addresses.', - example: null, field: 'destination.ip', - type: 'ip', originalValue: '10.47.8.200', values: ['10.47.8.200'], }, { - category: 'destination', - description: 'Packets sent from the destination to the source.', - example: '12', field: 'destination.packets', - type: 'long', originalValue: 4, values: ['4'], }, { - category: 'destination', - description: 'Port of the destination.', - example: null, field: 'destination.port', - type: 'long', originalValue: 902, values: ['902'], }, diff --git a/x-pack/legacy/plugins/siem/public/store/timeline/actions.ts b/x-pack/legacy/plugins/siem/public/store/timeline/actions.ts index 00977c959282a..6c36a5fae6e89 100644 --- a/x-pack/legacy/plugins/siem/public/store/timeline/actions.ts +++ b/x-pack/legacy/plugins/siem/public/store/timeline/actions.ts @@ -184,3 +184,5 @@ export const updateAutoSaveMsg = actionCreator<{ timelineId: string | null; newTimelineModel: TimelineModel | null; }>('UPDATE_AUTO_SAVE'); + +export const showCallOutUnauthorizedMsg = actionCreator('SHOW_CALL_OUT_UNAUTHORIZED_MSG'); diff --git a/x-pack/legacy/plugins/siem/public/store/timeline/epic.ts b/x-pack/legacy/plugins/siem/public/store/timeline/epic.ts index c65f91c99a80d..2b31ac9505d18 100644 --- a/x-pack/legacy/plugins/siem/public/store/timeline/epic.ts +++ b/x-pack/legacy/plugins/siem/public/store/timeline/epic.ts @@ -29,6 +29,7 @@ import { TimelineResult, } from '../../graphql/types'; import { AppApolloClient } from '../../lib/lib'; +import { addError } from '../app/actions'; import { NotesById } from '../app/model'; import { TimeRange } from '../inputs/model'; @@ -55,6 +56,7 @@ import { endTimelineSaving, createTimeline, addTimeline, + showCallOutUnauthorizedMsg, } from './actions'; import { TimelineModel } from './model'; import { epicPersistNote, timelineNoteActionsType } from './epic_note'; @@ -131,6 +133,9 @@ export const createTimelineEpic = (): Epic< withLatestFrom(timeline$), filter(([action, timeline]) => { const timelineId: TimelineModel = timeline[get('payload.id', action)]; + if (action.type === addError.type) { + return true; + } if (action.type === createTimeline.type) { myEpicTimelineId.setTimelineId(null); myEpicTimelineId.setTimelineVersion(null); @@ -186,6 +191,7 @@ export const createTimelineEpic = (): Epic< mergeMap(([result, recentTimeline]) => { const savedTimeline = recentTimeline[get('payload.id', action)]; const response: ResponseTimeline = get('data.persistTimeline', result); + const callOutMsg = response.code === 403 ? [showCallOutUnauthorizedMsg()] : []; return [ response.code === 409 @@ -202,6 +208,7 @@ export const createTimelineEpic = (): Epic< isSaving: false, }, }), + ...callOutMsg, endTimelineSaving({ id: get('payload.id', action), }), diff --git a/x-pack/legacy/plugins/siem/public/store/timeline/epic_favorite.ts b/x-pack/legacy/plugins/siem/public/store/timeline/epic_favorite.ts index 9e8201073737a..62b54b959f215 100644 --- a/x-pack/legacy/plugins/siem/public/store/timeline/epic_favorite.ts +++ b/x-pack/legacy/plugins/siem/public/store/timeline/epic_favorite.ts @@ -14,12 +14,13 @@ import { filter, mergeMap, withLatestFrom, startWith, takeUntil } from 'rxjs/ope import { persistTimelineFavoriteMutation } from '../../containers/timeline/favorite/persist.gql_query'; import { PersistTimelineFavoriteMutation, ResponseFavoriteTimeline } from '../../graphql/types'; - +import { addError } from '../app/actions'; import { endTimelineSaving, updateIsFavorite, updateTimeline, startTimelineSaving, + showCallOutUnauthorizedMsg, } from './actions'; import { dispatcherTimelinePersistQueue } from './epic_dispatcher_timeline_persistence_queue'; import { refetchQueries } from './refetch_queries'; @@ -53,7 +54,10 @@ export const epicPersistTimelineFavorite = ( mergeMap(([result, recentTimelines]) => { const savedTimeline = recentTimelines[get('payload.id', action)]; const response: ResponseFavoriteTimeline = get('data.persistFavorite', result); + const callOutMsg = response.code === 403 ? [showCallOutUnauthorizedMsg()] : []; + return [ + ...callOutMsg, updateTimeline({ id: get('payload.id', action), timeline: { @@ -73,6 +77,9 @@ export const epicPersistTimelineFavorite = ( action$.pipe( withLatestFrom(timeline$), filter(([checkAction, updatedTimeline]) => { + if (checkAction.type === addError.type) { + return true; + } if ( checkAction.type === endTimelineSaving.type && updatedTimeline[get('payload.id', checkAction)].savedObjectId != null diff --git a/x-pack/legacy/plugins/siem/public/store/timeline/epic_note.ts b/x-pack/legacy/plugins/siem/public/store/timeline/epic_note.ts index 2e47e0e4ab7ec..866617281c927 100644 --- a/x-pack/legacy/plugins/siem/public/store/timeline/epic_note.ts +++ b/x-pack/legacy/plugins/siem/public/store/timeline/epic_note.ts @@ -14,7 +14,7 @@ import { filter, mergeMap, switchMap, withLatestFrom, startWith, takeUntil } fro import { persistTimelineNoteMutation } from '../../containers/timeline/notes/persist.gql_query'; import { PersistTimelineNoteMutation, ResponseNote } from '../../graphql/types'; -import { updateNote } from '../app/actions'; +import { updateNote, addError } from '../app/actions'; import { NotesById } from '../app/model'; import { @@ -23,6 +23,7 @@ import { endTimelineSaving, updateTimeline, startTimelineSaving, + showCallOutUnauthorizedMsg, } from './actions'; import { myEpicTimelineId } from './my_epic_timeline_id'; import { refetchQueries } from './refetch_queries'; @@ -63,8 +64,10 @@ export const epicPersistNote = ( mergeMap(([result, recentTimeline, recentNotes]) => { const noteIdRedux = get('payload.noteId', action); const response: ResponseNote = get('data.persistNote', result); + const callOutMsg = response.code === 403 ? [showCallOutUnauthorizedMsg()] : []; return [ + ...callOutMsg, recentTimeline[get('payload.id', action)].savedObjectId == null ? updateTimeline({ id: get('payload.id', action), @@ -100,6 +103,9 @@ export const epicPersistNote = ( action$.pipe( withLatestFrom(timeline$), filter(([checkAction, updatedTimeline]) => { + if (checkAction.type === addError.type) { + return true; + } if ( checkAction.type === endTimelineSaving.type && updatedTimeline[get('payload.id', checkAction)].savedObjectId != null diff --git a/x-pack/legacy/plugins/siem/public/store/timeline/epic_pinned_event.ts b/x-pack/legacy/plugins/siem/public/store/timeline/epic_pinned_event.ts index 05451de91983b..8202c2f0dc49b 100644 --- a/x-pack/legacy/plugins/siem/public/store/timeline/epic_pinned_event.ts +++ b/x-pack/legacy/plugins/siem/public/store/timeline/epic_pinned_event.ts @@ -14,13 +14,14 @@ import { filter, mergeMap, startWith, withLatestFrom, takeUntil } from 'rxjs/ope import { persistTimelinePinnedEventMutation } from '../../containers/timeline/pinned_event/persist.gql_query'; import { PersistTimelinePinnedEventMutation, PinnedEvent } from '../../graphql/types'; - +import { addError } from '../app/actions'; import { pinEvent, endTimelineSaving, unPinEvent, updateTimeline, startTimelineSaving, + showCallOutUnauthorizedMsg, } from './actions'; import { myEpicTimelineId } from './my_epic_timeline_id'; import { refetchQueries } from './refetch_queries'; @@ -63,6 +64,7 @@ export const epicPersistPinnedEvent = ( mergeMap(([result, recentTimeline]) => { const savedTimeline = recentTimeline[get('payload.id', action)]; const response: PinnedEvent = get('data.persistPinnedEventOnTimeline', result); + const callOutMsg = response && response.code === 403 ? [showCallOutUnauthorizedMsg()] : []; return [ response != null @@ -94,6 +96,7 @@ export const epicPersistPinnedEvent = ( ), }, }), + ...callOutMsg, endTimelineSaving({ id: get('payload.id', action), }), @@ -104,6 +107,9 @@ export const epicPersistPinnedEvent = ( action$.pipe( withLatestFrom(timeline$), filter(([checkAction, updatedTimeline]) => { + if (checkAction.type === addError.type) { + return true; + } if ( checkAction.type === endTimelineSaving.type && updatedTimeline[get('payload.id', checkAction)].savedObjectId != null diff --git a/x-pack/legacy/plugins/siem/public/store/timeline/helpers.ts b/x-pack/legacy/plugins/siem/public/store/timeline/helpers.ts index 7fc2c2497b4a1..34759730d2563 100644 --- a/x-pack/legacy/plugins/siem/public/store/timeline/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/store/timeline/helpers.ts @@ -29,6 +29,7 @@ export const initialTimelineState: TimelineState = { timelineId: null, newTimelineModel: null, }, + showCallOutUnauthorizedMsg: false, }; interface AddTimelineHistoryParams { diff --git a/x-pack/legacy/plugins/siem/public/store/timeline/reducer.ts b/x-pack/legacy/plugins/siem/public/store/timeline/reducer.ts index 44615cb288396..7809245b9bb66 100644 --- a/x-pack/legacy/plugins/siem/public/store/timeline/reducer.ts +++ b/x-pack/legacy/plugins/siem/public/store/timeline/reducer.ts @@ -21,6 +21,7 @@ import { removeColumn, removeProvider, setKqlFilterQueryDraft, + showCallOutUnauthorizedMsg, showTimeline, startTimelineSaving, unPinEvent, @@ -89,6 +90,7 @@ export const initialTimelineState: TimelineState = { timelineId: null, newTimelineModel: null, }, + showCallOutUnauthorizedMsg: false, }; /** The reducer for all timeline actions */ @@ -348,4 +350,8 @@ export const timelineReducer = reducerWithInitialState(initialTimelineState) newTimelineModel, }, })) + .case(showCallOutUnauthorizedMsg, state => ({ + ...state, + showCallOutUnauthorizedMsg: true, + })) .build(); diff --git a/x-pack/legacy/plugins/siem/public/store/timeline/selectors.ts b/x-pack/legacy/plugins/siem/public/store/timeline/selectors.ts index 7890348aac4a7..8c6da2d356aa7 100644 --- a/x-pack/legacy/plugins/siem/public/store/timeline/selectors.ts +++ b/x-pack/legacy/plugins/siem/public/store/timeline/selectors.ts @@ -16,6 +16,9 @@ const selectTimelineById = (state: State): TimelineById => state.timeline.timeli const selectAutoSaveMsg = (state: State): AutoSavedWarningMsg => state.timeline.autoSavedWarningMsg; +const selectCallOutUnauthorizedMsg = (state: State): boolean => + state.timeline.showCallOutUnauthorizedMsg; + export const selectTimeline = (state: State, timelineId: string): TimelineModel => state.timeline.timelineById[timelineId]; @@ -29,6 +32,12 @@ export const timelineByIdSelector = createSelector( timelineById => timelineById ); +export const getShowCallOutUnauthorizedMsg = () => + createSelector( + selectCallOutUnauthorizedMsg, + showCallOutUnauthorizedMsg => showCallOutUnauthorizedMsg + ); + export const getTimelineByIdSelector = () => createSelector( selectTimeline, diff --git a/x-pack/legacy/plugins/siem/public/store/timeline/types.ts b/x-pack/legacy/plugins/siem/public/store/timeline/types.ts index 19cb7e8560a01..5395df6941973 100644 --- a/x-pack/legacy/plugins/siem/public/store/timeline/types.ts +++ b/x-pack/legacy/plugins/siem/public/store/timeline/types.ts @@ -22,4 +22,5 @@ export const EMPTY_TIMELINE_BY_ID: TimelineById = {}; // stable reference export interface TimelineState { timelineById: TimelineById; autoSavedWarningMsg: AutoSavedWarningMsg; + showCallOutUnauthorizedMsg: boolean; } diff --git a/x-pack/legacy/plugins/siem/server/graphql/events/schema.gql.ts b/x-pack/legacy/plugins/siem/server/graphql/events/schema.gql.ts index 376f4b7e96426..ab04fcc2192f4 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/events/schema.gql.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/events/schema.gql.ts @@ -48,11 +48,7 @@ export const eventsSchema = gql` } type DetailItem { - category: String! - description: String - example: String field: String! - type: String! values: ToStringArray originalValue: EsValue } diff --git a/x-pack/legacy/plugins/siem/server/graphql/pinned_event/schema.gql.ts b/x-pack/legacy/plugins/siem/server/graphql/pinned_event/schema.gql.ts index 7bd5d95a2b06f..a797cd6720af2 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/pinned_event/schema.gql.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/pinned_event/schema.gql.ts @@ -12,6 +12,8 @@ export const pinnedEventSchema = gql` ######################### type PinnedEvent { + code: Float + message: String pinnedEventId: ID! eventId: ID timelineId: ID diff --git a/x-pack/legacy/plugins/siem/server/graphql/timeline/schema.gql.ts b/x-pack/legacy/plugins/siem/server/graphql/timeline/schema.gql.ts index eb465fab5f18a..a417ecb82b2d2 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/timeline/schema.gql.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/timeline/schema.gql.ts @@ -201,6 +201,8 @@ export const timelineSchema = gql` } type ResponseFavoriteTimeline { + code: Float + message: String savedObjectId: String! version: String! favorite: [FavoriteTimelineResult!] diff --git a/x-pack/legacy/plugins/siem/server/graphql/types.ts b/x-pack/legacy/plugins/siem/server/graphql/types.ts index 6e14ba97f1c8a..b0c43011b2b21 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/types.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/types.ts @@ -108,6 +108,10 @@ export interface ResponseNotes { } export interface PinnedEvent { + code?: number | null; + + message?: string | null; + pinnedEventId: string; eventId?: string | null; @@ -875,16 +879,8 @@ export interface TimelineDetailsData { } export interface DetailItem { - category: string; - - description?: string | null; - - example?: string | null; - field: string; - type: string; - values?: ToStringArray | null; originalValue?: EsValue | null; @@ -1505,6 +1501,10 @@ export interface ResponseTimeline { } export interface ResponseFavoriteTimeline { + code?: number | null; + + message?: string | null; + savedObjectId: string; version: string; @@ -2391,6 +2391,10 @@ export namespace ResponseNotesResolvers { export namespace PinnedEventResolvers { export interface Resolvers { + code?: CodeResolver; + + message?: MessageResolver; + pinnedEventId?: PinnedEventIdResolver; eventId?: EventIdResolver; @@ -2410,6 +2414,16 @@ export namespace PinnedEventResolvers { version?: VersionResolver; } + export type CodeResolver< + R = number | null, + Parent = PinnedEvent, + Context = SiemContext + > = Resolver; + export type MessageResolver< + R = string | null, + Parent = PinnedEvent, + Context = SiemContext + > = Resolver; export type PinnedEventIdResolver< R = string, Parent = PinnedEvent, @@ -5204,46 +5218,18 @@ export namespace TimelineDetailsDataResolvers { export namespace DetailItemResolvers { export interface Resolvers { - category?: CategoryResolver; - - description?: DescriptionResolver; - - example?: ExampleResolver; - field?: FieldResolver; - type?: TypeResolver; - values?: ValuesResolver; originalValue?: OriginalValueResolver; } - export type CategoryResolver = Resolver< - R, - Parent, - Context - >; - export type DescriptionResolver< - R = string | null, - Parent = DetailItem, - Context = SiemContext - > = Resolver; - export type ExampleResolver< - R = string | null, - Parent = DetailItem, - Context = SiemContext - > = Resolver; export type FieldResolver = Resolver< R, Parent, Context >; - export type TypeResolver = Resolver< - R, - Parent, - Context - >; export type ValuesResolver< R = ToStringArray | null, Parent = DetailItem, @@ -7396,6 +7382,10 @@ export namespace ResponseTimelineResolvers { export namespace ResponseFavoriteTimelineResolvers { export interface Resolvers { + code?: CodeResolver; + + message?: MessageResolver; + savedObjectId?: SavedObjectIdResolver; version?: VersionResolver; @@ -7403,6 +7393,16 @@ export namespace ResponseFavoriteTimelineResolvers { favorite?: FavoriteResolver; } + export type CodeResolver< + R = number | null, + Parent = ResponseFavoriteTimeline, + Context = SiemContext + > = Resolver; + export type MessageResolver< + R = string | null, + Parent = ResponseFavoriteTimeline, + Context = SiemContext + > = Resolver; export type SavedObjectIdResolver< R = string, Parent = ResponseFavoriteTimeline, diff --git a/x-pack/legacy/plugins/siem/server/lib/events/elasticsearch_adapter.ts b/x-pack/legacy/plugins/siem/server/lib/events/elasticsearch_adapter.ts index e8d3c36cb5a5d..33c5b28393b9f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/events/elasticsearch_adapter.ts +++ b/x-pack/legacy/plugins/siem/server/lib/events/elasticsearch_adapter.ts @@ -27,17 +27,11 @@ import { TimelineDetailsData, TimelineEdges, } from '../../graphql/types'; -import { getDocumentation, getIndexAlias, hasDocumentation } from '../../utils/beat_schema'; import { baseCategoryFields } from '../../utils/beat_schema/8.0.0'; import { reduceFields } from '../../utils/build_query/reduce_fields'; import { mergeFieldsWithHit, inspectStringifyObject } from '../../utils/build_query'; import { eventFieldsMap } from '../ecs_fields'; -import { - FrameworkAdapter, - FrameworkRequest, - MappingProperties, - RequestOptionsPaginated, -} from '../framework'; +import { FrameworkAdapter, FrameworkRequest, RequestOptionsPaginated } from '../framework'; import { TermAggregation } from '../types'; import { buildDetailsQuery, buildQuery, buildTimelineQuery } from './query.dsl'; @@ -136,14 +130,11 @@ export class ElasticsearchEventsAdapter implements EventsAdapter { options: RequestDetailsOptions ): Promise { const dsl = buildDetailsQuery(options.indexName, options.eventId); - const [mapResponse, searchResponse] = await Promise.all([ - this.framework.callWithRequest(request, 'indices.getMapping', { - allowNoIndices: true, - ignoreUnavailable: true, - index: options.indexName, - }), - this.framework.callWithRequest(request, 'search', dsl), - ]); + const searchResponse = await this.framework.callWithRequest( + request, + 'search', + dsl + ); const sourceData = getOr({}, 'hits.hits.0._source', searchResponse); const hitsData = getOr({}, 'hits.hits.0', searchResponse); @@ -154,14 +145,7 @@ export class ElasticsearchEventsAdapter implements EventsAdapter { }; return { - data: getSchemaFromData( - { - ...addBasicElasticSearchProperties(), - ...getOr({}, [options.indexName, 'mappings', 'properties'], mapResponse), - }, - getDataFromHits(merge(sourceData, hitsData)), - getIndexAlias(options.defaultIndex, options.indexName) - ), + data: getDataFromHits(merge(sourceData, hitsData)), inspect, }; } @@ -317,50 +301,3 @@ const getDataFromHits = (sources: EventSource, category?: string, path?: string) } return accumulator; }, []); - -const getSchemaFromData = ( - properties: MappingProperties, - data: DetailItem[], - index: string, - path?: string -): DetailItem[] => - !isEmpty(properties) - ? Object.keys(properties).reduce((accumulator, property) => { - const item = get(property, properties); - const field = path ? `${path}.${property}` : property; - const dataFilterItem = data.filter(dataItem => dataItem.field === field); - if (item.properties == null && dataFilterItem.length === 1) { - const dataItem = dataFilterItem[0]; - const dataFromMapping = { - type: get([property, 'type'], properties), - }; - return [ - ...accumulator, - { - ...dataItem, - ...(hasDocumentation(index, field) - ? merge(getDocumentation(index, field), dataFromMapping) - : dataFromMapping), - }, - ]; - } else if (item.properties != null) { - return [...accumulator, ...getSchemaFromData(item.properties, data, index, field)]; - } - return accumulator; - }, []) - : data; - -const addBasicElasticSearchProperties = () => ({ - _id: { - type: 'keyword', - }, - _index: { - type: 'keyword', - }, - _type: { - type: 'keyword', - }, - _score: { - type: 'long', - }, -}); diff --git a/x-pack/legacy/plugins/siem/server/lib/events/mock.ts b/x-pack/legacy/plugins/siem/server/lib/events/mock.ts index 3ee0667be61cb..b6b6a1f9c2934 100644 --- a/x-pack/legacy/plugins/siem/server/lib/events/mock.ts +++ b/x-pack/legacy/plugins/siem/server/lib/events/mock.ts @@ -2883,695 +2883,529 @@ export const mockTimelineDetailsResult = { response: [JSON.stringify(mockTimelineDetailsInspectResponse, null, 2)], }, data: [ - { - category: '_id', - field: '_id', - values: 'TUfUymkBCQofM5eXGBYL', - originalValue: 'TUfUymkBCQofM5eXGBYL', - description: 'Each document has an _id that uniquely identifies it', - example: 'Y-6TfmcB0WOhS6qyMv3s', - footnote: '', - group: 1, - level: 'core', - name: '_id', - required: true, - type: 'keyword', - }, - { - category: '_index', - field: '_index', - values: 'auditbeat-8.0.0-2019.03.29-000003', - originalValue: 'auditbeat-8.0.0-2019.03.29-000003', - description: - 'An index is like a ‘database’ in a relational database. It has a mapping which defines multiple types. An index is a logical namespace which maps to one or more primary shards and can have zero or more replica shards.', - example: 'auditbeat-8.0.0-2019.02.19-000001', - footnote: '', - group: 1, - level: 'core', - name: '_index', - required: true, - type: 'keyword', - }, - { - category: '_type', - field: '_type', - values: '_doc', - originalValue: '_doc', - type: 'keyword', - }, - { - category: '_score', - field: '_score', - values: 1, - originalValue: 1, - type: 'long', - }, { category: '@timestamp', field: '@timestamp', values: '2019-03-29T19:01:23.420Z', originalValue: '2019-03-29T19:01:23.420Z', - description: - 'Date/time when the event originated. For log events this is the date/time when the event was generated, and not when it was read. Required field for all events.', - example: '2016-05-23T08:05:34.853Z', - name: '@timestamp', - type: 'date', - }, - { - category: 'agent', - field: 'agent.ephemeral_id', - values: '6d541d59-52d0-4e70-b4d2-2660c0a99ff7', - originalValue: '6d541d59-52d0-4e70-b4d2-2660c0a99ff7', - description: - 'Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but `agent.id` does not.', - example: '8a4f500f', - name: 'ephemeral_id', - type: 'keyword', - }, - { - category: 'agent', - field: 'agent.hostname', - values: 'zeek-london', - originalValue: 'zeek-london', - type: 'keyword', }, { - category: 'agent', - field: 'agent.id', - values: 'cc1f4183-36c6-45c4-b21b-7ce70c3572db', - originalValue: 'cc1f4183-36c6-45c4-b21b-7ce70c3572db', - description: - 'Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.', - example: '8a4f500d', - name: 'id', - type: 'keyword', + category: 'service', + field: 'service.type', + values: 'auditd', + originalValue: 'auditd', }, { - category: 'agent', - field: 'agent.type', - values: 'auditbeat', - originalValue: 'auditbeat', - description: - 'Type of the agent. The agent type stays always the same and should be given by the agent used. In case of Filebeat the agent would always be Filebeat also if two Filebeat instances are run on the same machine.', - example: 'filebeat', - name: 'type', - type: 'keyword', + category: 'user', + field: 'user.audit.id', + values: 'unset', + originalValue: 'unset', }, { - category: 'agent', - field: 'agent.version', - values: '8.0.0', - originalValue: '8.0.0', - description: 'Version of the agent.', - example: '6.0.0-rc2', - name: 'version', - type: 'keyword', + category: 'user', + field: 'user.group.id', + values: '0', + originalValue: '0', }, { - category: 'auditd', - field: 'auditd.data.a0', - values: 'ffffff9c', - originalValue: 'ffffff9c', - type: 'keyword', + category: 'user', + field: 'user.group.name', + values: 'root', + originalValue: 'root', }, { - category: 'auditd', - field: 'auditd.data.a1', - values: '7fe0f63df220', - originalValue: '7fe0f63df220', - type: 'keyword', + category: 'user', + field: 'user.effective.group.id', + values: '0', + originalValue: '0', }, { - category: 'auditd', - field: 'auditd.data.a2', - values: '80000', - originalValue: '80000', - type: 'keyword', + category: 'user', + field: 'user.effective.group.name', + values: 'root', + originalValue: 'root', }, { - category: 'auditd', - field: 'auditd.data.a3', + category: 'user', + field: 'user.effective.id', values: '0', originalValue: '0', - type: 'keyword', }, { - category: 'auditd', - field: 'auditd.data.arch', - values: 'x86_64', - originalValue: 'x86_64', - type: 'keyword', + category: 'user', + field: 'user.effective.name', + values: 'root', + originalValue: 'root', }, { - category: 'auditd', - field: 'auditd.data.exit', - values: '12', - originalValue: '12', - type: 'keyword', + category: 'user', + field: 'user.filesystem.group.name', + values: 'root', + originalValue: 'root', }, { - category: 'auditd', - field: 'auditd.data.syscall', - values: 'openat', - originalValue: 'openat', - type: 'keyword', + category: 'user', + field: 'user.filesystem.group.id', + values: '0', + originalValue: '0', }, { - category: 'auditd', - field: 'auditd.data.tty', - values: '(none)', - originalValue: '(none)', - type: 'keyword', + category: 'user', + field: 'user.filesystem.name', + values: 'root', + originalValue: 'root', }, { - category: 'auditd', - field: 'auditd.message_type', - values: 'syscall', - originalValue: 'syscall', - type: 'keyword', + category: 'user', + field: 'user.filesystem.id', + values: '0', + originalValue: '0', }, { - category: 'auditd', - field: 'auditd.result', - values: 'success', - originalValue: 'success', - type: 'keyword', + category: 'user', + field: 'user.saved.group.id', + values: '0', + originalValue: '0', }, { - category: 'auditd', - field: 'auditd.sequence', - values: 8817905, - originalValue: 8817905, - type: 'long', + category: 'user', + field: 'user.saved.group.name', + values: 'root', + originalValue: 'root', }, { - category: 'auditd', - field: 'auditd.session', - values: 'unset', - originalValue: 'unset', - type: 'keyword', + category: 'user', + field: 'user.saved.id', + values: '0', + originalValue: '0', }, { - category: 'auditd', - field: 'auditd.summary.actor.primary', - values: 'unset', - originalValue: 'unset', - type: 'keyword', + category: 'user', + field: 'user.saved.name', + values: 'root', + originalValue: 'root', }, { - category: 'auditd', - field: 'auditd.summary.actor.secondary', + category: 'user', + field: 'user.id', + values: '0', + originalValue: '0', + }, + { + category: 'user', + field: 'user.name', values: 'root', originalValue: 'root', - type: 'keyword', }, { - category: 'auditd', - field: 'auditd.summary.how', + category: 'process', + field: 'process.executable', values: '/root/go/src/github.com/elastic/beats/x-pack/auditbeat/auditbeat', originalValue: '/root/go/src/github.com/elastic/beats/x-pack/auditbeat/auditbeat', - type: 'keyword', }, { - category: 'auditd', - field: 'auditd.summary.object.primary', - values: '/etc/passwd', - originalValue: '/etc/passwd', - type: 'keyword', + category: 'process', + field: 'process.working_directory', + values: '/root/go/src/github.com/elastic/beats/x-pack/auditbeat', + originalValue: '/root/go/src/github.com/elastic/beats/x-pack/auditbeat', }, { - category: 'auditd', - field: 'auditd.summary.object.type', - values: 'file', - originalValue: 'file', - type: 'keyword', + category: 'process', + field: 'process.pid', + values: 15990, + originalValue: 15990, }, { - category: 'cloud', - field: 'cloud.instance.id', - values: '136398786', - originalValue: '136398786', - description: 'Instance ID of the host machine.', - example: 'i-1234567890abcdef0', - name: 'instance.id', - type: 'keyword', + category: 'process', + field: 'process.ppid', + values: 1, + originalValue: 1, }, { - category: 'cloud', - field: 'cloud.provider', - values: 'digitalocean', - originalValue: 'digitalocean', - description: 'Name of the cloud provider. Example values are ec2, gce, or digitalocean.', - example: 'ec2', - name: 'provider', - type: 'keyword', + category: 'process', + field: 'process.title', + values: + '/root/go/src/github.com/elastic/beats/x-pack/auditbeat/auditbeat -e -c /root/go/src/github.com/elastic/beats/x-pack/auditbeat/au', + originalValue: + '/root/go/src/github.com/elastic/beats/x-pack/auditbeat/auditbeat -e -c /root/go/src/github.com/elastic/beats/x-pack/auditbeat/au', }, { - category: 'cloud', - field: 'cloud.region', - values: 'lon1', - originalValue: 'lon1', - description: 'Region in which this host is running.', - example: 'us-east-1', - name: 'region', - type: 'keyword', + category: 'process', + field: 'process.name', + values: 'auditbeat', + originalValue: 'auditbeat', }, { - category: 'ecs', - field: 'ecs.version', - values: '1.0.0', - originalValue: '1.0.0', - description: - 'ECS version this event conforms to. `ecs.version` is a required field and must exist in all events. When querying across multiple indices -- which may conform to slightly different ECS versions -- this field lets integrations adjust to the schema version of the events. The current version is 1.0.0-beta2 .', - example: '1.0.0-beta2', - name: 'version', - type: 'keyword', + category: 'host', + field: 'host.architecture', + values: 'x86_64', + originalValue: 'x86_64', }, { - category: 'event', - field: 'event.action', - values: 'opened-file', - originalValue: 'opened-file', - description: - 'The action captured by the event. This describes the information in the event. It is more specific than `event.category`. Examples are `group-add`, `process-started`, `file-created`. The value is normally defined by the implementer.', - example: 'user-password-change', - name: 'action', - type: 'keyword', + category: 'host', + field: 'host.os.name', + values: 'Ubuntu', + originalValue: 'Ubuntu', }, { - category: 'event', - field: 'event.category', - values: 'audit-rule', - originalValue: 'audit-rule', - description: - 'Event category. This contains high-level information about the contents of the event. It is more generic than `event.action`, in the sense that typically a category contains multiple actions. Warning: In future versions of ECS, we plan to provide a list of acceptable values for this field, please use with caution.', - example: 'user-management', - name: 'category', - type: 'keyword', + category: 'host', + field: 'host.os.kernel', + values: '4.15.0-45-generic', + originalValue: '4.15.0-45-generic', }, { - category: 'event', - field: 'event.module', - values: 'auditd', - originalValue: 'auditd', - description: - 'Name of the module this data is coming from. This information is coming from the modules used in Beats or Logstash.', - example: 'mysql', - name: 'module', - type: 'keyword', + category: 'host', + field: 'host.os.codename', + values: 'bionic', + originalValue: 'bionic', }, { - category: 'event', - field: 'event.original', - values: [ - 'type=SYSCALL msg=audit(1553886083.420:8817905): arch=c000003e syscall=257 success=yes exit=12 a0=ffffff9c a1=7fe0f63df220 a2=80000 a3=0 items=1 ppid=1 pid=15990 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="auditbeat" exe="/root/go/src/github.com/elastic/beats/x-pack/auditbeat/auditbeat" key=(null)', - 'type=CWD msg=audit(1553886083.420:8817905): cwd="/root/go/src/github.com/elastic/beats/x-pack/auditbeat"', - 'type=PATH msg=audit(1553886083.420:8817905): item=0 name="/etc/passwd" inode=3926 dev=fc:01 mode=0100644 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0000000000000000 cap_fi=0000000000000000 cap_fe=0 cap_fver=0', - 'type=PROCTITLE msg=audit(1553886083.420:8817905): proctitle=2F726F6F742F676F2F7372632F6769746875622E636F6D2F656C61737469632F62656174732F782D7061636B2F6175646974626561742F617564697462656174002D65002D63002F726F6F742F676F2F7372632F6769746875622E636F6D2F656C61737469632F62656174732F782D7061636B2F6175646974626561742F6175', - ], - originalValue: [ - 'type=SYSCALL msg=audit(1553886083.420:8817905): arch=c000003e syscall=257 success=yes exit=12 a0=ffffff9c a1=7fe0f63df220 a2=80000 a3=0 items=1 ppid=1 pid=15990 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="auditbeat" exe="/root/go/src/github.com/elastic/beats/x-pack/auditbeat/auditbeat" key=(null)', - 'type=CWD msg=audit(1553886083.420:8817905): cwd="/root/go/src/github.com/elastic/beats/x-pack/auditbeat"', - 'type=PATH msg=audit(1553886083.420:8817905): item=0 name="/etc/passwd" inode=3926 dev=fc:01 mode=0100644 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0000000000000000 cap_fi=0000000000000000 cap_fe=0 cap_fver=0', - 'type=PROCTITLE msg=audit(1553886083.420:8817905): proctitle=2F726F6F742F676F2F7372632F6769746875622E636F6D2F656C61737469632F62656174732F782D7061636B2F6175646974626561742F617564697462656174002D65002D63002F726F6F742F676F2F7372632F6769746875622E636F6D2F656C61737469632F62656174732F782D7061636B2F6175646974626561742F6175', - ], - description: - 'Raw text message of entire event. Used to demonstrate log integrity. This field is not indexed and doc_values are disabled. It cannot be searched, but it can be retrieved from `_source`.', - example: - 'Sep 19 08:26:10 host CEF:0|Security| threatmanager|1.0|100| worm successfully stopped|10|src=10.0.0.1 dst=2.1.2.2spt=1232', - name: 'original', - type: 'keyword', + category: 'host', + field: 'host.os.platform', + values: 'ubuntu', + originalValue: 'ubuntu', }, { - category: 'file', - field: 'file.device', - values: '00:00', - originalValue: '00:00', - description: 'Device that is the source of the file.', - name: 'device', - type: 'keyword', + category: 'host', + field: 'host.os.version', + values: '18.04.2 LTS (Bionic Beaver)', + originalValue: '18.04.2 LTS (Bionic Beaver)', }, { - category: 'file', - field: 'file.gid', - values: '0', - originalValue: '0', - description: 'Primary group ID (GID) of the file.', - name: 'gid', - type: 'keyword', + category: 'host', + field: 'host.os.family', + values: 'debian', + originalValue: 'debian', + }, + { + category: 'host', + field: 'host.id', + values: '7c21f5ed03b04d0299569d221fe18bbc', + originalValue: '7c21f5ed03b04d0299569d221fe18bbc', + }, + { + category: 'host', + field: 'host.name', + values: 'zeek-london', + originalValue: 'zeek-london', + }, + { + category: 'host', + field: 'host.ip', + values: ['46.101.3.136', '10.16.0.5', 'fe80::4066:42ff:fe19:b3b9'], + originalValue: ['46.101.3.136', '10.16.0.5', 'fe80::4066:42ff:fe19:b3b9'], + }, + { + category: 'host', + field: 'host.mac', + values: ['42:66:42:19:b3:b9'], + originalValue: ['42:66:42:19:b3:b9'], + }, + { + category: 'host', + field: 'host.hostname', + values: 'zeek-london', + originalValue: 'zeek-london', + }, + { + category: 'cloud', + field: 'cloud.provider', + values: 'digitalocean', + originalValue: 'digitalocean', + }, + { + category: 'cloud', + field: 'cloud.instance.id', + values: '136398786', + originalValue: '136398786', + }, + { + category: 'cloud', + field: 'cloud.region', + values: 'lon1', + originalValue: 'lon1', }, { category: 'file', - field: 'file.group', - values: 'root', - originalValue: 'root', - description: 'Primary group name of the file.', - name: 'group', - type: 'keyword', + field: 'file.device', + values: '00:00', + originalValue: '00:00', }, { category: 'file', field: 'file.inode', values: '3926', originalValue: '3926', - description: 'Inode representing the file in the filesystem.', - name: 'inode', - type: 'keyword', }, { category: 'file', field: 'file.mode', values: '0644', originalValue: '0644', - description: 'Mode of the file in octal representation.', - example: 416, - name: 'mode', - type: 'keyword', - }, - { - category: 'file', - field: 'file.owner', - values: 'root', - originalValue: 'root', - description: "File owner's username.", - name: 'owner', - type: 'keyword', - }, - { - category: 'file', - field: 'file.path', - values: '/etc/passwd', - originalValue: '/etc/passwd', - description: 'Path to the file.', - name: 'path', - type: 'keyword', }, { category: 'file', field: 'file.uid', values: '0', originalValue: '0', - description: 'The user ID (UID) or security identifier (SID) of the file owner.', - name: 'uid', - type: 'keyword', }, { - category: 'host', - field: 'host.architecture', - values: 'x86_64', - originalValue: 'x86_64', - description: 'Operating system architecture.', - example: 'x86_64', - name: 'architecture', - type: 'keyword', + category: 'file', + field: 'file.gid', + values: '0', + originalValue: '0', }, { - category: 'host', - field: 'host.hostname', - values: 'zeek-london', - originalValue: 'zeek-london', - description: - 'Hostname of the host. It normally contains what the `hostname` command returns on the host machine.', - name: 'hostname', - type: 'keyword', + category: 'file', + field: 'file.owner', + values: 'root', + originalValue: 'root', }, { - category: 'host', - field: 'host.id', - values: '7c21f5ed03b04d0299569d221fe18bbc', - originalValue: '7c21f5ed03b04d0299569d221fe18bbc', - description: - 'Unique host id. As hostname is not always unique, use values that are meaningful in your environment. Example: The current usage of `beat.name`.', - name: 'id', - type: 'keyword', + category: 'file', + field: 'file.group', + values: 'root', + originalValue: 'root', }, { - category: 'host', - field: 'host.ip', - values: ['46.101.3.136', '10.16.0.5', 'fe80::4066:42ff:fe19:b3b9'], - originalValue: ['46.101.3.136', '10.16.0.5', 'fe80::4066:42ff:fe19:b3b9'], - description: 'Host ip address.', - name: 'ip', - type: 'ip', + category: 'file', + field: 'file.path', + values: '/etc/passwd', + originalValue: '/etc/passwd', }, { - category: 'host', - field: 'host.mac', - values: ['42:66:42:19:b3:b9'], - originalValue: ['42:66:42:19:b3:b9'], - description: 'Host mac address.', - name: 'mac', - type: 'keyword', + category: 'auditd', + field: 'auditd.session', + values: 'unset', + originalValue: 'unset', }, { - category: 'host', - field: 'host.name', - values: 'zeek-london', - originalValue: 'zeek-london', - description: - 'Name of the host. It can contain what `hostname` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use.', - name: 'name', - type: 'keyword', + category: 'auditd', + field: 'auditd.data.tty', + values: '(none)', + originalValue: '(none)', }, { - category: 'host', - field: 'host.os.codename', - values: 'bionic', - originalValue: 'bionic', - type: 'keyword', + category: 'auditd', + field: 'auditd.data.a3', + values: '0', + originalValue: '0', }, { - category: 'host', - field: 'host.os.family', - values: 'debian', - originalValue: 'debian', - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - name: 'family', - type: 'keyword', + category: 'auditd', + field: 'auditd.data.a2', + values: '80000', + originalValue: '80000', }, { - category: 'host', - field: 'host.os.kernel', - values: '4.15.0-45-generic', - originalValue: '4.15.0-45-generic', - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', - name: 'kernel', - type: 'keyword', + category: 'auditd', + field: 'auditd.data.syscall', + values: 'openat', + originalValue: 'openat', }, { - category: 'host', - field: 'host.os.name', - values: 'Ubuntu', - originalValue: 'Ubuntu', - description: 'Operating system name, without the version.', - example: 'Mac OS X', - name: 'name', - type: 'keyword', + category: 'auditd', + field: 'auditd.data.a1', + values: '7fe0f63df220', + originalValue: '7fe0f63df220', }, { - category: 'host', - field: 'host.os.platform', - values: 'ubuntu', - originalValue: 'ubuntu', - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - name: 'platform', - type: 'keyword', + category: 'auditd', + field: 'auditd.data.a0', + values: 'ffffff9c', + originalValue: 'ffffff9c', }, { - category: 'host', - field: 'host.os.version', - values: '18.04.2 LTS (Bionic Beaver)', - originalValue: '18.04.2 LTS (Bionic Beaver)', - description: 'Operating system version as a raw string.', - example: '10.14.1', - name: 'version', - type: 'keyword', + category: 'auditd', + field: 'auditd.data.arch', + values: 'x86_64', + originalValue: 'x86_64', }, { - category: 'process', - field: 'process.executable', - values: '/root/go/src/github.com/elastic/beats/x-pack/auditbeat/auditbeat', - originalValue: '/root/go/src/github.com/elastic/beats/x-pack/auditbeat/auditbeat', - type: 'keyword', + category: 'auditd', + field: 'auditd.data.exit', + values: '12', + originalValue: '12', }, { - category: 'process', - field: 'process.name', - values: 'auditbeat', - originalValue: 'auditbeat', - type: 'keyword', + category: 'auditd', + field: 'auditd.summary.actor.primary', + values: 'unset', + originalValue: 'unset', }, { - category: 'process', - field: 'process.pid', - values: 15990, - originalValue: 15990, - type: 'long', + category: 'auditd', + field: 'auditd.summary.actor.secondary', + values: 'root', + originalValue: 'root', }, { - category: 'process', - field: 'process.ppid', - values: 1, - originalValue: 1, - type: 'long', + category: 'auditd', + field: 'auditd.summary.object.primary', + values: '/etc/passwd', + originalValue: '/etc/passwd', }, { - category: 'process', - field: 'process.title', - values: - '/root/go/src/github.com/elastic/beats/x-pack/auditbeat/auditbeat -e -c /root/go/src/github.com/elastic/beats/x-pack/auditbeat/au', - originalValue: - '/root/go/src/github.com/elastic/beats/x-pack/auditbeat/auditbeat -e -c /root/go/src/github.com/elastic/beats/x-pack/auditbeat/au', - type: 'keyword', + category: 'auditd', + field: 'auditd.summary.object.type', + values: 'file', + originalValue: 'file', }, { - category: 'process', - field: 'process.working_directory', - values: '/root/go/src/github.com/elastic/beats/x-pack/auditbeat', - originalValue: '/root/go/src/github.com/elastic/beats/x-pack/auditbeat', - type: 'keyword', + category: 'auditd', + field: 'auditd.summary.how', + values: '/root/go/src/github.com/elastic/beats/x-pack/auditbeat/auditbeat', + originalValue: '/root/go/src/github.com/elastic/beats/x-pack/auditbeat/auditbeat', }, { - category: 'service', - field: 'service.type', - values: 'auditd', - originalValue: 'auditd', - description: - 'The type of the service data is collected from. The type can be used to group and correlate logs and metrics from one service type. Example: If logs or metrics are collected from Elasticsearch, `service.type` would be `elasticsearch`.', - example: 'elasticsearch', - name: 'type', - type: 'keyword', + category: 'auditd', + field: 'auditd.paths', + values: [ + { + rdev: '00:00', + cap_fe: '0', + nametype: 'NORMAL', + ogid: '0', + ouid: '0', + inode: '3926', + item: '0', + mode: '0100644', + name: '/etc/passwd', + cap_fi: '0000000000000000', + cap_fp: '0000000000000000', + cap_fver: '0', + dev: 'fc:01', + }, + ], + originalValue: [ + { + rdev: '00:00', + cap_fe: '0', + nametype: 'NORMAL', + ogid: '0', + ouid: '0', + inode: '3926', + item: '0', + mode: '0100644', + name: '/etc/passwd', + cap_fi: '0000000000000000', + cap_fp: '0000000000000000', + cap_fver: '0', + dev: 'fc:01', + }, + ], }, { - category: 'user', - field: 'user.audit.id', - values: 'unset', - originalValue: 'unset', - type: 'keyword', + category: 'auditd', + field: 'auditd.message_type', + values: 'syscall', + originalValue: 'syscall', }, { - category: 'user', - field: 'user.effective.group.id', - values: '0', - originalValue: '0', - type: 'keyword', + category: 'auditd', + field: 'auditd.sequence', + values: 8817905, + originalValue: 8817905, }, { - category: 'user', - field: 'user.effective.group.name', - values: 'root', - originalValue: 'root', - type: 'keyword', + category: 'auditd', + field: 'auditd.result', + values: 'success', + originalValue: 'success', }, { - category: 'user', - field: 'user.effective.id', - values: '0', - originalValue: '0', - type: 'keyword', + category: 'event', + field: 'event.category', + values: 'audit-rule', + originalValue: 'audit-rule', }, { - category: 'user', - field: 'user.effective.name', - values: 'root', - originalValue: 'root', - type: 'keyword', + category: 'event', + field: 'event.action', + values: 'opened-file', + originalValue: 'opened-file', }, { - category: 'user', - field: 'user.filesystem.group.id', - values: '0', - originalValue: '0', - type: 'keyword', + category: 'event', + field: 'event.original', + values: [ + 'type=SYSCALL msg=audit(1553886083.420:8817905): arch=c000003e syscall=257 success=yes exit=12 a0=ffffff9c a1=7fe0f63df220 a2=80000 a3=0 items=1 ppid=1 pid=15990 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="auditbeat" exe="/root/go/src/github.com/elastic/beats/x-pack/auditbeat/auditbeat" key=(null)', + 'type=CWD msg=audit(1553886083.420:8817905): cwd="/root/go/src/github.com/elastic/beats/x-pack/auditbeat"', + 'type=PATH msg=audit(1553886083.420:8817905): item=0 name="/etc/passwd" inode=3926 dev=fc:01 mode=0100644 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0000000000000000 cap_fi=0000000000000000 cap_fe=0 cap_fver=0', + 'type=PROCTITLE msg=audit(1553886083.420:8817905): proctitle=2F726F6F742F676F2F7372632F6769746875622E636F6D2F656C61737469632F62656174732F782D7061636B2F6175646974626561742F617564697462656174002D65002D63002F726F6F742F676F2F7372632F6769746875622E636F6D2F656C61737469632F62656174732F782D7061636B2F6175646974626561742F6175', + ], + originalValue: [ + 'type=SYSCALL msg=audit(1553886083.420:8817905): arch=c000003e syscall=257 success=yes exit=12 a0=ffffff9c a1=7fe0f63df220 a2=80000 a3=0 items=1 ppid=1 pid=15990 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="auditbeat" exe="/root/go/src/github.com/elastic/beats/x-pack/auditbeat/auditbeat" key=(null)', + 'type=CWD msg=audit(1553886083.420:8817905): cwd="/root/go/src/github.com/elastic/beats/x-pack/auditbeat"', + 'type=PATH msg=audit(1553886083.420:8817905): item=0 name="/etc/passwd" inode=3926 dev=fc:01 mode=0100644 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0000000000000000 cap_fi=0000000000000000 cap_fe=0 cap_fver=0', + 'type=PROCTITLE msg=audit(1553886083.420:8817905): proctitle=2F726F6F742F676F2F7372632F6769746875622E636F6D2F656C61737469632F62656174732F782D7061636B2F6175646974626561742F617564697462656174002D65002D63002F726F6F742F676F2F7372632F6769746875622E636F6D2F656C61737469632F62656174732F782D7061636B2F6175646974626561742F6175', + ], }, { - category: 'user', - field: 'user.filesystem.group.name', - values: 'root', - originalValue: 'root', - type: 'keyword', + category: 'event', + field: 'event.module', + values: 'auditd', + originalValue: 'auditd', }, { - category: 'user', - field: 'user.filesystem.id', - values: '0', - originalValue: '0', - type: 'keyword', + category: 'ecs', + field: 'ecs.version', + values: '1.0.0', + originalValue: '1.0.0', }, { - category: 'user', - field: 'user.filesystem.name', - values: 'root', - originalValue: 'root', - type: 'keyword', + category: 'agent', + field: 'agent.ephemeral_id', + values: '6d541d59-52d0-4e70-b4d2-2660c0a99ff7', + originalValue: '6d541d59-52d0-4e70-b4d2-2660c0a99ff7', }, { - category: 'user', - field: 'user.group.id', - values: '0', - originalValue: '0', - description: 'Unique identifier for the group on the system/platform.', - name: 'id', - type: 'keyword', + category: 'agent', + field: 'agent.hostname', + values: 'zeek-london', + originalValue: 'zeek-london', }, { - category: 'user', - field: 'user.group.name', - values: 'root', - originalValue: 'root', - description: 'Name of the group.', - name: 'name', - type: 'keyword', + category: 'agent', + field: 'agent.id', + values: 'cc1f4183-36c6-45c4-b21b-7ce70c3572db', + originalValue: 'cc1f4183-36c6-45c4-b21b-7ce70c3572db', }, { - category: 'user', - field: 'user.id', - values: '0', - originalValue: '0', - description: 'One or multiple unique identifiers of the user.', - name: 'id', - type: 'keyword', + category: 'agent', + field: 'agent.version', + values: '8.0.0', + originalValue: '8.0.0', }, { - category: 'user', - field: 'user.name', - values: 'root', - originalValue: 'root', - description: 'Short name or login of the user.', - example: 'albert', - name: 'name', - type: 'keyword', + category: 'agent', + field: 'agent.type', + values: 'auditbeat', + originalValue: 'auditbeat', }, { - category: 'user', - field: 'user.saved.group.id', - values: '0', - originalValue: '0', - type: 'keyword', + category: '_index', + field: '_index', + values: 'auditbeat-8.0.0-2019.03.29-000003', + originalValue: 'auditbeat-8.0.0-2019.03.29-000003', }, { - category: 'user', - field: 'user.saved.group.name', - values: 'root', - originalValue: 'root', - type: 'keyword', + category: '_type', + field: '_type', + values: '_doc', + originalValue: '_doc', }, { - category: 'user', - field: 'user.saved.id', - values: '0', - originalValue: '0', - type: 'keyword', + category: '_id', + field: '_id', + values: 'TUfUymkBCQofM5eXGBYL', + originalValue: 'TUfUymkBCQofM5eXGBYL', }, { - category: 'user', - field: 'user.saved.name', - values: 'root', - originalValue: 'root', - type: 'keyword', + category: '_score', + field: '_score', + values: 1, + originalValue: 1, }, ], }; diff --git a/x-pack/legacy/plugins/siem/server/lib/note/saved_object.ts b/x-pack/legacy/plugins/siem/server/lib/note/saved_object.ts index 474ae1b829914..7c3b5a3f02612 100644 --- a/x-pack/legacy/plugins/siem/server/lib/note/saved_object.ts +++ b/x-pack/legacy/plugins/siem/server/lib/note/saved_object.ts @@ -8,11 +8,18 @@ import { failure } from 'io-ts/lib/PathReporter'; import { RequestAuth } from 'hapi'; import { Legacy } from 'kibana'; import { getOr } from 'lodash/fp'; +import uuid from 'uuid'; import { SavedObjectsFindOptions } from 'src/core/server'; import { Pick3 } from '../../../common/utility_types'; -import { PageInfoNote, ResponseNote, ResponseNotes, SortNote } from '../../graphql/types'; +import { + PageInfoNote, + ResponseNote, + ResponseNotes, + SortNote, + NoteResult, +} from '../../graphql/types'; import { FrameworkRequest, internalFrameworkRequest } from '../framework'; import { SavedNote, NoteSavedObjectRuntimeType, NoteSavedObject } from './types'; import { noteSavedObjectType } from './saved_object_mappings'; @@ -156,6 +163,20 @@ export class Note { ), }; } catch (err) { + if (getOr(null, 'output.statusCode', err) === 403) { + const noteToReturn: NoteResult = { + ...note, + noteId: uuid.v1(), + version: '', + timelineId: '', + timelineVersion: '', + }; + return { + code: 403, + message: err.message, + note: noteToReturn, + }; + } throw err; } } diff --git a/x-pack/legacy/plugins/siem/server/lib/pinned_event/saved_object.ts b/x-pack/legacy/plugins/siem/server/lib/pinned_event/saved_object.ts index f62f36d32f52e..d07a51d7f94c0 100644 --- a/x-pack/legacy/plugins/siem/server/lib/pinned_event/saved_object.ts +++ b/x-pack/legacy/plugins/siem/server/lib/pinned_event/saved_object.ts @@ -18,7 +18,7 @@ import { PinnedEventSavedObjectRuntimeType, SavedPinnedEvent, } from './types'; -import { PageInfoNote, SortNote } from '../../graphql/types'; +import { PageInfoNote, SortNote, PinnedEvent as PinnedEventResponse } from '../../graphql/types'; import { pinnedEventSavedObjectType, timelineSavedObjectType } from '../../saved_objects'; import { pickSavedTimeline } from '../timeline/pick_saved_timeline'; import { convertSavedObjectToSavedTimeline } from '../timeline/convert_saved_object_to_savedtimeline'; @@ -96,7 +96,7 @@ export class PinnedEvent { pinnedEventId: string | null, eventId: string, timelineId: string | null - ): Promise { + ): Promise { try { if (pinnedEventId == null) { const timelineVersionSavedObject = @@ -148,6 +148,17 @@ export class PinnedEvent { await this.deletePinnedEventOnTimeline(request, [pinnedEventId]); return null; } catch (err) { + if (getOr(null, 'output.statusCode', err) === 403) { + return pinnedEventId != null + ? { + code: 403, + message: err.message, + pinnedEventId: eventId, + timelineId: '', + timelineVersion: '', + } + : null; + } throw err; } } diff --git a/x-pack/legacy/plugins/siem/server/lib/timeline/saved_object.ts b/x-pack/legacy/plugins/siem/server/lib/timeline/saved_object.ts index 433fc175b642f..4d3203e2c5570 100644 --- a/x-pack/legacy/plugins/siem/server/lib/timeline/saved_object.ts +++ b/x-pack/legacy/plugins/siem/server/lib/timeline/saved_object.ts @@ -15,6 +15,7 @@ import { PageInfoTimeline, SortTimeline, ResponseFavoriteTimeline, + TimelineResult, } from '../../graphql/types'; import { FrameworkRequest, internalFrameworkRequest } from '../framework'; import { NoteSavedObject } from '../note/types'; @@ -77,54 +78,68 @@ export class Timeline { request: FrameworkRequest, timelineId: string | null ): Promise { - let timeline: SavedTimeline = {}; - if (timelineId != null) { - const { - eventIdToNoteIds, - notes, - noteIds, - pinnedEventIds, - pinnedEventsSaveObject, - savedObjectId, - version, - ...savedTimeline - } = await this.getBasicSavedTimeline(request, timelineId); - timelineId = savedObjectId; - timeline = savedTimeline; - } const userName = getOr(null, 'credentials.username', request[internalFrameworkRequest].auth); const fullName = getOr(null, 'credentials.fullname', request[internalFrameworkRequest].auth); - const userFavoriteTimeline = { - keySearch: userName != null ? convertStringToBase64(userName) : null, - favoriteDate: new Date().valueOf(), - fullName, - userName, - }; - if (timeline.favorite != null) { - const alreadyExistsTimelineFavoriteByUser = timeline.favorite.findIndex( - user => user.userName === userName - ); + try { + let timeline: SavedTimeline = {}; + if (timelineId != null) { + const { + eventIdToNoteIds, + notes, + noteIds, + pinnedEventIds, + pinnedEventsSaveObject, + savedObjectId, + version, + ...savedTimeline + } = await this.getBasicSavedTimeline(request, timelineId); + timelineId = savedObjectId; + timeline = savedTimeline; + } - timeline.favorite = - alreadyExistsTimelineFavoriteByUser > -1 - ? [ - ...timeline.favorite.slice(0, alreadyExistsTimelineFavoriteByUser), - ...timeline.favorite.slice(alreadyExistsTimelineFavoriteByUser + 1), - ] - : [...timeline.favorite, userFavoriteTimeline]; - } else if (timeline.favorite == null) { - timeline.favorite = [userFavoriteTimeline]; - } + const userFavoriteTimeline = { + keySearch: userName != null ? convertStringToBase64(userName) : null, + favoriteDate: new Date().valueOf(), + fullName, + userName, + }; + if (timeline.favorite != null) { + const alreadyExistsTimelineFavoriteByUser = timeline.favorite.findIndex( + user => user.userName === userName + ); - const persistResponse = await this.persistTimeline(request, timelineId, null, timeline); - return { - savedObjectId: persistResponse.timeline.savedObjectId, - version: persistResponse.timeline.version, - favorite: - persistResponse.timeline.favorite != null - ? persistResponse.timeline.favorite.filter(fav => fav.userName === userName) - : [], - }; + timeline.favorite = + alreadyExistsTimelineFavoriteByUser > -1 + ? [ + ...timeline.favorite.slice(0, alreadyExistsTimelineFavoriteByUser), + ...timeline.favorite.slice(alreadyExistsTimelineFavoriteByUser + 1), + ] + : [...timeline.favorite, userFavoriteTimeline]; + } else if (timeline.favorite == null) { + timeline.favorite = [userFavoriteTimeline]; + } + + const persistResponse = await this.persistTimeline(request, timelineId, null, timeline); + return { + savedObjectId: persistResponse.timeline.savedObjectId, + version: persistResponse.timeline.version, + favorite: + persistResponse.timeline.favorite != null + ? persistResponse.timeline.favorite.filter(fav => fav.userName === userName) + : [], + }; + } catch (err) { + if (getOr(null, 'output.statusCode', err) === 403) { + return { + savedObjectId: '', + version: '', + favorite: [], + code: 403, + message: err.message, + }; + } + throw err; + } } public async persistTimeline( @@ -133,27 +148,26 @@ export class Timeline { version: string | null, timeline: SavedTimeline ): Promise { - if (timelineId == null) { - // Create new timeline - return { - code: 200, - message: 'success', - timeline: convertSavedObjectToSavedTimeline( - await this.libs.savedObjects - .getScopedSavedObjectsClient(request[internalFrameworkRequest]) - .create( - timelineSavedObjectType, - pickSavedTimeline( - timelineId, - timeline, - request[internalFrameworkRequest].auth || null - ) - ) - ), - }; - } - try { + if (timelineId == null) { + // Create new timeline + return { + code: 200, + message: 'success', + timeline: convertSavedObjectToSavedTimeline( + await this.libs.savedObjects + .getScopedSavedObjectsClient(request[internalFrameworkRequest]) + .create( + timelineSavedObjectType, + pickSavedTimeline( + timelineId, + timeline, + request[internalFrameworkRequest].auth || null + ) + ) + ), + }; + } // Update Timeline await this.libs.savedObjects .getScopedSavedObjectsClient(request[internalFrameworkRequest]) @@ -171,12 +185,26 @@ export class Timeline { timeline: await this.getSavedTimeline(request, timelineId), }; } catch (err) { - if (this.libs.savedObjects.SavedObjectsClient.errors.isConflictError(err)) { + if ( + timelineId != null && + this.libs.savedObjects.SavedObjectsClient.errors.isConflictError(err) + ) { return { code: 409, message: err.message, timeline: await this.getSavedTimeline(request, timelineId), }; + } else if (getOr(null, 'output.statusCode', err) === 403) { + const timelineToReturn: TimelineResult = { + ...timeline, + savedObjectId: '', + version: '', + }; + return { + code: 403, + message: err.message, + timeline: timelineToReturn, + }; } throw err; } diff --git a/x-pack/test/api_integration/apis/siem/timeline_details.ts b/x-pack/test/api_integration/apis/siem/timeline_details.ts index 9f48c75ece0bc..d59c45c338c8c 100644 --- a/x-pack/test/api_integration/apis/siem/timeline_details.ts +++ b/x-pack/test/api_integration/apis/siem/timeline_details.ts @@ -15,10 +15,7 @@ import { import { KbnTestProvider } from './types'; type DetailsData = Array< - Pick< - DetailItem, - 'category' | 'description' | 'example' | 'field' | 'type' | 'values' | 'originalValue' - > & { + Pick & { __typename: string; } >; @@ -28,618 +25,277 @@ const INDEX_NAME = 'filebeat-7.0.0-iot-2019.06'; const ID = 'QRhG1WgBqd-n62SwZYDT'; const EXPECTED_DATA: DetailItem[] = [ { - category: '_id', - description: 'Each document has an _id that uniquely identifies it', - example: 'Y-6TfmcB0WOhS6qyMv3s', - field: '_id', - type: 'keyword', - originalValue: 'QRhG1WgBqd-n62SwZYDT', - values: ['QRhG1WgBqd-n62SwZYDT'], - }, - { - category: '_index', - description: - 'An index is like a ‘database’ in a relational database. It has a mapping which defines multiple types. An index is a logical namespace which maps to one or more primary shards and can have zero or more replica shards.', - example: 'auditbeat-8.0.0-2019.02.19-000001', - field: '_index', - type: 'keyword', - originalValue: 'filebeat-7.0.0-iot-2019.06', - values: ['filebeat-7.0.0-iot-2019.06'], - }, - { - category: '_type', - description: null, - example: null, - field: '_type', - type: 'keyword', - originalValue: '_doc', - values: ['_doc'], - }, - { - category: '_score', - description: null, - example: null, - field: '_score', - type: 'long', - originalValue: 1, - values: ['1'], - }, - { - category: '@timestamp', - description: - 'Date/time when the event originated. For log events this is the date/time when the event was generated, and not when it was read. Required field for all events.', - example: '2016-05-23T08:05:34.853Z', field: '@timestamp', - type: 'date', - originalValue: '2019-02-10T02:39:44.107Z', values: ['2019-02-10T02:39:44.107Z'], + originalValue: '2019-02-10T02:39:44.107Z', }, + { field: '@version', values: ['1'], originalValue: '1' }, { - category: '@version', - description: null, - example: null, - field: '@version', - type: 'keyword', - originalValue: '1', - values: ['1'], - }, - { - category: 'agent', - description: - 'Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but `agent.id` does not.', - example: '8a4f500f', field: 'agent.ephemeral_id', - type: 'keyword', - originalValue: '909cd6a1-527d-41a5-9585-a7fb5386f851', values: ['909cd6a1-527d-41a5-9585-a7fb5386f851'], + originalValue: '909cd6a1-527d-41a5-9585-a7fb5386f851', }, { - category: 'agent', - description: null, - example: null, field: 'agent.hostname', - type: 'keyword', - originalValue: 'raspberrypi', values: ['raspberrypi'], + originalValue: 'raspberrypi', }, { - category: 'agent', - description: - 'Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.', - example: '8a4f500d', field: 'agent.id', - type: 'keyword', - originalValue: '4d3ea604-27e5-4ec7-ab64-44f82285d776', values: ['4d3ea604-27e5-4ec7-ab64-44f82285d776'], + originalValue: '4d3ea604-27e5-4ec7-ab64-44f82285d776', }, { - category: 'agent', - description: - 'Type of the agent. The agent type stays always the same and should be given by the agent used. In case of Filebeat the agent would always be Filebeat also if two Filebeat instances are run on the same machine.', - example: 'filebeat', field: 'agent.type', - type: 'keyword', - originalValue: 'filebeat', values: ['filebeat'], + originalValue: 'filebeat', }, + { field: 'agent.version', values: ['7.0.0'], originalValue: '7.0.0' }, { - category: 'agent', - description: 'Version of the agent.', - example: '6.0.0-rc2', - field: 'agent.version', - type: 'keyword', - originalValue: '7.0.0', - values: ['7.0.0'], - }, - { - category: 'destination', - description: 'Destination domain.', - example: null, field: 'destination.domain', - type: 'keyword', - originalValue: 's3-iad-2.cf.dash.row.aiv-cdn.net', values: ['s3-iad-2.cf.dash.row.aiv-cdn.net'], + originalValue: 's3-iad-2.cf.dash.row.aiv-cdn.net', }, { - category: 'destination', - description: 'IP address of the destination. Can be one or multiple IPv4 or IPv6 addresses.', - example: null, field: 'destination.ip', - type: 'ip', - originalValue: '10.100.7.196', values: ['10.100.7.196'], + originalValue: '10.100.7.196', }, + { field: 'destination.port', values: ['40684'], originalValue: 40684 }, { - category: 'destination', - description: 'Port of the destination.', - example: null, - field: 'destination.port', - type: 'long', - originalValue: 40684, - values: ['40684'], - }, - { - category: 'ecs', - description: - 'ECS version this event conforms to. `ecs.version` is a required field and must exist in all events. When querying across multiple indices -- which may conform to slightly different ECS versions -- this field lets integrations adjust to the schema version of the events. The current version is 1.0.0-beta2 .', - example: '1.0.0-beta2', field: 'ecs.version', - type: 'keyword', - originalValue: '1.0.0-beta2', values: ['1.0.0-beta2'], + originalValue: '1.0.0-beta2', }, { - category: 'event', - description: - 'Name of the dataset. The concept of a `dataset` (fileset / metricset) is used in Beats as a subset of modules. It contains the information which is currently stored in metricset.name and metricset.module or fileset.name.', - example: 'stats', field: 'event.dataset', - type: 'keyword', - originalValue: 'suricata.eve', values: ['suricata.eve'], + originalValue: 'suricata.eve', }, { - category: 'event', - description: - 'event.end contains the date when the event ended or when the activity was last observed.', - example: null, field: 'event.end', - type: 'date', - originalValue: '2019-02-10T02:39:44.107Z', values: ['2019-02-10T02:39:44.107Z'], + originalValue: '2019-02-10T02:39:44.107Z', }, + { field: 'event.kind', values: ['event'], originalValue: 'event' }, { - category: 'event', - description: - 'The kind of the event. This gives information about what type of information the event contains, without being specific to the contents of the event. Examples are `event`, `state`, `alarm`. Warning: In future versions of ECS, we plan to provide a list of acceptable values for this field, please use with caution.', - example: 'state', - field: 'event.kind', - type: 'keyword', - originalValue: 'event', - values: ['event'], - }, - { - category: 'event', - description: - 'Name of the module this data is coming from. This information is coming from the modules used in Beats or Logstash.', - example: 'mysql', field: 'event.module', - type: 'keyword', - originalValue: 'suricata', values: ['suricata'], + originalValue: 'suricata', }, { - category: 'event', - description: 'Reserved for future usage. Please avoid using this field for user data.', - example: null, field: 'event.type', - type: 'keyword', - originalValue: 'fileinfo', values: ['fileinfo'], + originalValue: 'fileinfo', }, { - category: 'file', - description: 'Path to the file.', - example: null, field: 'file.path', - type: 'keyword', - originalValue: - '/dm/2$XTMWANo0Q2RZKlH-95UoAahZrOg~/0a9a/bf72/e1da/4c20-919e-0cbabcf7bfe8/75f50c57-d25f-4e97-9e37-01b9f5caa293_audio_13.mp4', 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', }, + { field: 'file.size', values: ['48277'], originalValue: 48277 }, + { field: 'fileset.name', values: ['eve'], originalValue: 'eve' }, + { field: 'flow.locality', values: ['public'], originalValue: 'public' }, { - category: 'file', - description: 'File size in bytes (field is only added when `type` is `file`).', - example: null, - field: 'file.size', - type: 'long', - originalValue: 48277, - values: ['48277'], - }, - { - category: 'fileset', - description: null, - example: null, - field: 'fileset.name', - type: 'keyword', - originalValue: 'eve', - values: ['eve'], - }, - { - category: 'flow', - description: null, - example: null, - field: 'flow.locality', - type: 'keyword', - originalValue: 'public', - values: ['public'], - }, - { - category: 'host', - description: 'Operating system architecture.', - example: 'x86_64', field: 'host.architecture', - type: 'keyword', - originalValue: 'armv7l', values: ['armv7l'], + originalValue: 'armv7l', }, { - category: 'host', - description: - 'Hostname of the host. It normally contains what the `hostname` command returns on the host machine.', - example: null, field: 'host.hostname', - type: 'keyword', - originalValue: 'raspberrypi', values: ['raspberrypi'], + originalValue: 'raspberrypi', }, { - category: 'host', - description: - 'Unique host id. As hostname is not always unique, use values that are meaningful in your environment. Example: The current usage of `beat.name`.', - example: null, field: 'host.id', - type: 'keyword', - originalValue: 'b19a781f683541a7a25ee345133aa399', values: ['b19a781f683541a7a25ee345133aa399'], + originalValue: 'b19a781f683541a7a25ee345133aa399', }, { - category: 'host', - description: - 'Name of the host. It can contain what `hostname` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use.', - example: null, field: 'host.name', - type: 'keyword', - originalValue: 'raspberrypi', values: ['raspberrypi'], + originalValue: 'raspberrypi', }, { - category: 'host', - description: null, - example: null, field: 'host.os.codename', - type: 'keyword', - originalValue: 'stretch', values: ['stretch'], + originalValue: 'stretch', }, + { field: 'host.os.family', values: [''], originalValue: '' }, { - category: 'host', - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - field: 'host.os.family', - type: 'keyword', - originalValue: '', - values: [''], - }, - { - category: 'host', - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', field: 'host.os.kernel', - type: 'keyword', - originalValue: '4.14.50-v7+', values: ['4.14.50-v7+'], + originalValue: '4.14.50-v7+', }, { - category: 'host', - description: 'Operating system name, without the version.', - example: 'Mac OS X', field: 'host.os.name', - type: 'keyword', - originalValue: 'Raspbian GNU/Linux', values: ['Raspbian GNU/Linux'], + originalValue: 'Raspbian GNU/Linux', }, { - category: 'host', - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', field: 'host.os.platform', - type: 'keyword', - originalValue: 'raspbian', values: ['raspbian'], + originalValue: 'raspbian', }, { - category: 'host', - description: 'Operating system version as a raw string.', - example: '10.14.1', field: 'host.os.version', - type: 'keyword', - originalValue: '9 (stretch)', values: ['9 (stretch)'], + originalValue: '9 (stretch)', }, + { field: 'http.request.method', values: ['get'], originalValue: 'get' }, { - category: 'http', - description: - 'Http request method. The field value must be normalized to lowercase for querying. See "Lowercase Capitalization" in the "Implementing ECS" section.', - example: 'get, post, put', - field: 'http.request.method', - type: 'keyword', - originalValue: 'get', - values: ['get'], - }, - { - category: 'http', - description: 'Size in bytes of the response body.', - example: '887', field: 'http.response.body.bytes', - type: 'long', - originalValue: 48277, values: ['48277'], + originalValue: 48277, }, { - category: 'http', - description: 'Http response status code.', - example: '404', field: 'http.response.status_code', - type: 'long', - originalValue: 206, values: ['206'], + originalValue: 206, }, + { field: 'input.type', values: ['log'], originalValue: 'log' }, { - category: 'input', - description: null, - example: null, - field: 'input.type', - type: 'keyword', - originalValue: 'log', - values: ['log'], - }, - { - category: 'labels', - description: null, - example: null, field: 'labels.pipeline', - type: 'keyword', - originalValue: 'filebeat-7.0.0-suricata-eve-pipeline', values: ['filebeat-7.0.0-suricata-eve-pipeline'], + originalValue: 'filebeat-7.0.0-suricata-eve-pipeline', }, { - category: 'log', - description: null, - example: null, field: 'log.file.path', - type: 'keyword', - originalValue: '/var/log/suricata/eve.json', values: ['/var/log/suricata/eve.json'], + originalValue: '/var/log/suricata/eve.json', }, { - category: 'log', - description: null, - example: null, field: 'log.offset', - type: 'long', - originalValue: 1856288115, values: ['1856288115'], + originalValue: 1856288115, }, + { field: 'network.name', values: ['iot'], originalValue: 'iot' }, + { field: 'network.protocol', values: ['http'], originalValue: 'http' }, + { field: 'network.transport', values: ['tcp'], originalValue: 'tcp' }, { - category: 'network', - description: 'Name given by operators to sections of their network.', - example: 'Guest Wifi', - field: 'network.name', - type: 'keyword', - originalValue: 'iot', - values: ['iot'], - }, - { - category: 'network', - description: - 'L7 Network protocol name. ex. http, lumberjack, transport protocol. The field value must be normalized to lowercase for querying. See "Lowercase Capitalization" in the "Implementing ECS" section.', - example: 'http', - field: 'network.protocol', - type: 'keyword', - originalValue: 'http', - values: ['http'], - }, - { - category: 'network', - description: - 'Same as network.iana_number, but instead using the Keyword name of the transport layer (udp, tcp, ipv6-icmp, etc.) The field value must be normalized to lowercase for querying. See "Lowercase Capitalization" in the "Implementing ECS" section.', - example: 'tcp', - field: 'network.transport', - type: 'keyword', - originalValue: 'tcp', - values: ['tcp'], - }, - { - category: 'service', - description: - 'The type of the service data is collected from. The type can be used to group and correlate logs and metrics from one service type. Example: If logs or metrics are collected from Elasticsearch, `service.type` would be `elasticsearch`.', - example: 'elasticsearch', field: 'service.type', - type: 'keyword', - originalValue: 'suricata', values: ['suricata'], + originalValue: 'suricata', }, + { field: 'source.as.num', values: ['16509'], originalValue: 16509 }, { - category: 'source', - description: null, - example: null, - field: 'source.as.num', - type: 'long', - originalValue: 16509, - values: ['16509'], - }, - { - category: 'source', - description: null, - example: null, field: 'source.as.org', - type: 'keyword', - originalValue: 'Amazon.com, Inc.', values: ['Amazon.com, Inc.'], + originalValue: 'Amazon.com, Inc.', }, { - category: 'source', - description: 'Source domain.', - example: null, field: 'source.domain', - type: 'keyword', - originalValue: 'server-54-239-219-210.jfk51.r.cloudfront.net', values: ['server-54-239-219-210.jfk51.r.cloudfront.net'], + originalValue: 'server-54-239-219-210.jfk51.r.cloudfront.net', }, { - category: 'source', - description: 'City name.', - example: 'Montreal', field: 'source.geo.city_name', - type: 'keyword', - originalValue: 'Seattle', values: ['Seattle'], + originalValue: 'Seattle', }, { - category: 'source', - description: 'Name of the continent.', - example: 'North America', field: 'source.geo.continent_name', - type: 'keyword', - originalValue: 'North America', values: ['North America'], + originalValue: 'North America', }, { - category: 'source', - description: 'Country ISO code.', - example: 'CA', field: 'source.geo.country_iso_code', - type: 'keyword', - originalValue: 'US', values: ['US'], + originalValue: 'US', + }, + { + field: 'source.geo.location.lat', + values: ['47.6103'], + originalValue: 47.6103, + }, + { + field: 'source.geo.location.lon', + values: ['-122.3341'], + originalValue: -122.3341, }, { - category: 'source', - description: 'Region ISO code.', - example: 'CA-QC', field: 'source.geo.region_iso_code', - type: 'keyword', - originalValue: 'US-WA', values: ['US-WA'], + originalValue: 'US-WA', }, { - category: 'source', - description: 'Region name.', - example: 'Quebec', field: 'source.geo.region_name', - type: 'keyword', - originalValue: 'Washington', values: ['Washington'], + originalValue: 'Washington', }, { - category: 'source', - description: 'IP address of the source. Can be one or multiple IPv4 or IPv6 addresses.', - example: null, field: 'source.ip', - type: 'ip', - originalValue: '54.239.219.210', values: ['54.239.219.210'], + originalValue: '54.239.219.210', }, + { field: 'source.port', values: ['80'], originalValue: 80 }, { - category: 'source', - description: 'Port of the source.', - example: null, - field: 'source.port', - type: 'long', - originalValue: 80, - values: ['80'], - }, - { - category: 'suricata', - description: null, - example: null, field: 'suricata.eve.fileinfo.state', - type: 'keyword', - originalValue: 'CLOSED', values: ['CLOSED'], + originalValue: 'CLOSED', }, { - category: 'suricata', - description: null, - example: null, field: 'suricata.eve.fileinfo.tx_id', - type: 'long', - originalValue: 301, values: ['301'], + originalValue: 301, }, { - category: 'suricata', - description: null, - example: null, field: 'suricata.eve.flow_id', - type: 'keyword', - originalValue: 196625917175466, values: ['196625917175466'], + originalValue: 196625917175466, }, { - category: 'suricata', - description: null, - example: null, field: 'suricata.eve.http.http_content_type', - type: 'keyword', - originalValue: 'video/mp4', values: ['video/mp4'], + originalValue: 'video/mp4', }, { - category: 'suricata', - description: null, - example: null, field: 'suricata.eve.http.protocol', - type: 'keyword', - originalValue: 'HTTP/1.1', values: ['HTTP/1.1'], + originalValue: 'HTTP/1.1', }, { - category: 'suricata', - description: null, - example: null, field: 'suricata.eve.in_iface', - type: 'keyword', - originalValue: 'eth0', values: ['eth0'], + originalValue: 'eth0', }, + { field: 'tags', values: ['suricata'], originalValue: ['suricata'] }, { - category: 'tags', - description: 'List of keywords used to tag each event.', - example: '["production", "env2"]', - field: 'tags', - type: 'keyword', - originalValue: ['suricata'], - values: ['suricata'], - }, - { - category: 'url', - description: - 'Domain of the request, such as "www.elastic.co". In some cases a URL may refer to an IP and/or port directly, without a domain name. In this case, the IP address would go to the `domain` field.', - example: 'www.elastic.co', field: 'url.domain', - type: 'keyword', - originalValue: 's3-iad-2.cf.dash.row.aiv-cdn.net', values: ['s3-iad-2.cf.dash.row.aiv-cdn.net'], + originalValue: 's3-iad-2.cf.dash.row.aiv-cdn.net', }, { - category: 'url', - description: - 'Unmodified original url as seen in the event source. Note that in network monitoring, the observed URL may be a full URL, whereas in access logs, the URL is often just represented as a path. This field is meant to represent the URL as it was observed, complete or not.', - example: 'https://www.elastic.co:443/search?q=elasticsearch#top or /search?q=elasticsearch', field: 'url.original', - type: 'keyword', - originalValue: - '/dm/2$XTMWANo0Q2RZKlH-95UoAahZrOg~/0a9a/bf72/e1da/4c20-919e-0cbabcf7bfe8/75f50c57-d25f-4e97-9e37-01b9f5caa293_audio_13.mp4', 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', }, { - category: 'url', - description: 'Path of the request, such as "/search".', - example: null, field: 'url.path', - type: 'keyword', - originalValue: - '/dm/2$XTMWANo0Q2RZKlH-95UoAahZrOg~/0a9a/bf72/e1da/4c20-919e-0cbabcf7bfe8/75f50c57-d25f-4e97-9e37-01b9f5caa293_audio_13.mp4', 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', + }, + { + field: '_index', + values: ['filebeat-7.0.0-iot-2019.06'], + originalValue: 'filebeat-7.0.0-iot-2019.06', + }, + { field: '_type', values: ['_doc'], originalValue: '_doc' }, + { + field: '_id', + values: ['QRhG1WgBqd-n62SwZYDT'], + originalValue: 'QRhG1WgBqd-n62SwZYDT', }, + { field: '_score', values: ['1'], originalValue: 1 }, ]; const timelineDetailsTests: KbnTestProvider = ({ getService }) => {