From 7da3f522f99d50f7dbe3e562a15106d9b2f32afe Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Mon, 5 Oct 2020 12:18:06 +0100 Subject: [PATCH] [Security Solution] Untitled Timeline created when first action is to add note (#78988) (#79424) * init tests * Untitled Timeline created * remove console * fix from server side * set timeline status to draft if created by saving notes * add unit test Co-authored-by: Elastic Machine Co-authored-by: Elastic Machine --- .../lib/timeline/pick_saved_timeline.test.ts | 239 ++++++++++++++++++ .../lib/timeline/pick_saved_timeline.ts | 2 +- 2 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/security_solution/server/lib/timeline/pick_saved_timeline.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/timeline/pick_saved_timeline.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/pick_saved_timeline.test.ts new file mode 100644 index 0000000000000..92d2a9c5a3077 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/timeline/pick_saved_timeline.test.ts @@ -0,0 +1,239 @@ +/* + * 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 { AuthenticatedUser } from '../../../../security/common/model'; + +import { TimelineStatus, TimelineType } from '../../../common/types/timeline'; + +import { pickSavedTimeline } from './pick_saved_timeline'; + +describe('pickSavedTimeline', () => { + const mockDateNow = new Date('2020-04-03T23:00:00.000Z').valueOf(); + const getMockSavedTimeline = () => ({ + savedObjectId: '7af80430-03f4-11eb-9d9d-ffba20fabba8', + version: 'WzQ0ODgsMV0=', + created: 1601563413330, + createdBy: 'Elastic', + updated: 1601563454756, + updatedBy: 'Elastic', + dateRange: { start: '2020-09-30T14:42:30.094Z', end: '2020-10-01T14:42:30.094Z' }, + columns: [ + { columnHeaderType: 'not-filtered', id: '@timestamp' }, + { columnHeaderType: 'not-filtered', id: 'message' }, + { columnHeaderType: 'not-filtered', id: 'event.category' }, + { columnHeaderType: 'not-filtered', id: 'event.action' }, + { columnHeaderType: 'not-filtered', id: 'host.name' }, + { columnHeaderType: 'not-filtered', id: 'source.ip' }, + { columnHeaderType: 'not-filtered', id: 'destination.ip' }, + { columnHeaderType: 'not-filtered', id: 'user.name' }, + ], + indexNames: [ + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + '.siem-signals-angelachuang-default', + ], + description: 'hhh', + templateTimelineVersion: null, + eventType: 'all', + filters: [], + sort: { sortDirection: 'desc', columnId: '@timestamp' }, + title: 'title', + kqlMode: 'filter', + timelineType: TimelineType.default, + savedQueryId: null, + kqlQuery: { filterQuery: null }, + dataProviders: [], + templateTimelineId: null, + eventNotes: [], + globalNotes: [ + { + noteId: '7ba7a520-03f4-11eb-9d9d-ffba20fabba8', + version: 'WzQ0ODEsMV0=', + note: '789', + timelineId: '7af80430-03f4-11eb-9d9d-ffba20fabba8', + created: 1601563414477, + createdBy: 'Elastic', + updated: 1601563414477, + updatedBy: 'Elastic', + }, + ], + pinnedEventIds: [], + }); + + beforeAll(() => { + Date = (jest.fn(() => ({ + valueOf: jest.fn().mockReturnValue(mockDateNow), + })) as unknown) as DateConstructor; + }); + + afterAll(() => { + ((Date as unknown) as jest.Mock).mockRestore(); + }); + + describe('Set create / update time correctly ', () => { + test('Creating a timeline', () => { + const savedTimeline = getMockSavedTimeline(); + const timelineId = null; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.created).toEqual(mockDateNow); + expect(result.updated).toEqual(mockDateNow); + }); + + test('Updating a timeline', () => { + const savedTimeline = getMockSavedTimeline(); + const timelineId = savedTimeline.savedObjectId; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.created).toEqual(savedTimeline.created); + expect(result.updated).toEqual(mockDateNow); + }); + }); + + describe('Set userInfo correctly ', () => { + test('Creating a timeline', () => { + const savedTimeline = getMockSavedTimeline(); + const timelineId = null; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.createdBy).toEqual(userInfo.username); + expect(result.updatedBy).toEqual(userInfo.username); + }); + + test('Updating a timeline', () => { + const savedTimeline = getMockSavedTimeline(); + const timelineId = savedTimeline.savedObjectId; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.createdBy).toEqual(savedTimeline.createdBy); + expect(result.updatedBy).toEqual(userInfo.username); + }); + }); + + describe('Set status correctly ', () => { + test('Creating a timeline with title', () => { + const savedTimeline = getMockSavedTimeline(); + const timelineId = null; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.status).toEqual(TimelineStatus.active); + }); + + test('Creating a timeline without title', () => { + const savedTimeline = { ...getMockSavedTimeline(), title: null }; + const timelineId = null; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.status).toEqual(TimelineStatus.draft); + }); + + test('Updating a timeline with a new title', () => { + const savedTimeline = getMockSavedTimeline(); + const timelineId = savedTimeline.savedObjectId; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.status).toEqual(TimelineStatus.active); + }); + + test('Updating a timeline without title', () => { + const savedTimeline = getMockSavedTimeline(); + const timelineId = savedTimeline.savedObjectId; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.status).toEqual(TimelineStatus.active); + }); + + test('Updating an immutable timeline with a new title', () => { + const savedTimeline = { ...getMockSavedTimeline(), status: TimelineStatus.immutable }; + const timelineId = savedTimeline.savedObjectId; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.status).toEqual(TimelineStatus.immutable); + }); + + test('Creating a draft timeline with title', () => { + const savedTimeline = { ...getMockSavedTimeline(), status: TimelineStatus.draft }; + const timelineId = null; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.status).toEqual(TimelineStatus.active); + }); + + test('Creating a draft timeline without title', () => { + const savedTimeline = { + ...getMockSavedTimeline(), + title: null, + status: TimelineStatus.draft, + }; + const timelineId = null; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.status).toEqual(TimelineStatus.draft); + }); + + test('Updating an untitled draft timeline with a title', () => { + const savedTimeline = { ...getMockSavedTimeline(), status: TimelineStatus.draft }; + const timelineId = savedTimeline.savedObjectId; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.status).toEqual(TimelineStatus.active); + }); + + test('Updating a draft timeline with a new title', () => { + const savedTimeline = { ...getMockSavedTimeline(), status: TimelineStatus.draft }; + const timelineId = savedTimeline.savedObjectId; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.status).toEqual(TimelineStatus.active); + }); + + test('Updating a draft timeline without title', () => { + const savedTimeline = { ...getMockSavedTimeline(), status: TimelineStatus.draft }; + const timelineId = savedTimeline.savedObjectId; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.status).toEqual(TimelineStatus.active); + }); + }); + + test('Set timelineType correctly ', () => { + const savedTimeline = getMockSavedTimeline(); + const timelineId = null; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.timelineType).toEqual(TimelineType.default); + expect(result.status).toEqual(TimelineStatus.active); + expect(result.templateTimelineId).toBeNull(); + expect(result.templateTimelineVersion).toBeNull(); + }); + + test('Set excludedRowRendererIds correctly ', () => { + const savedTimeline = getMockSavedTimeline(); + const timelineId = null; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.excludedRowRendererIds).toEqual([]); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/timeline/pick_saved_timeline.ts b/x-pack/plugins/security_solution/server/lib/timeline/pick_saved_timeline.ts index d3d7783dc9385..6c664f55eccf8 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/pick_saved_timeline.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/pick_saved_timeline.ts @@ -27,7 +27,7 @@ export const pickSavedTimeline = ( savedTimeline.updatedBy = userInfo?.username ?? UNAUTHENTICATED_USER; } - if (savedTimeline.status === TimelineStatus.draft) { + if (savedTimeline.status === TimelineStatus.draft || savedTimeline.status == null) { savedTimeline.status = !isEmpty(savedTimeline.title) ? TimelineStatus.active : TimelineStatus.draft;