Skip to content

Commit 7da3f52

Browse files
[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 <elasticmachine@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
1 parent f1c78b7 commit 7da3f52

File tree

2 files changed

+240
-1
lines changed

2 files changed

+240
-1
lines changed
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
import { AuthenticatedUser } from '../../../../security/common/model';
7+
8+
import { TimelineStatus, TimelineType } from '../../../common/types/timeline';
9+
10+
import { pickSavedTimeline } from './pick_saved_timeline';
11+
12+
describe('pickSavedTimeline', () => {
13+
const mockDateNow = new Date('2020-04-03T23:00:00.000Z').valueOf();
14+
const getMockSavedTimeline = () => ({
15+
savedObjectId: '7af80430-03f4-11eb-9d9d-ffba20fabba8',
16+
version: 'WzQ0ODgsMV0=',
17+
created: 1601563413330,
18+
createdBy: 'Elastic',
19+
updated: 1601563454756,
20+
updatedBy: 'Elastic',
21+
dateRange: { start: '2020-09-30T14:42:30.094Z', end: '2020-10-01T14:42:30.094Z' },
22+
columns: [
23+
{ columnHeaderType: 'not-filtered', id: '@timestamp' },
24+
{ columnHeaderType: 'not-filtered', id: 'message' },
25+
{ columnHeaderType: 'not-filtered', id: 'event.category' },
26+
{ columnHeaderType: 'not-filtered', id: 'event.action' },
27+
{ columnHeaderType: 'not-filtered', id: 'host.name' },
28+
{ columnHeaderType: 'not-filtered', id: 'source.ip' },
29+
{ columnHeaderType: 'not-filtered', id: 'destination.ip' },
30+
{ columnHeaderType: 'not-filtered', id: 'user.name' },
31+
],
32+
indexNames: [
33+
'auditbeat-*',
34+
'endgame-*',
35+
'filebeat-*',
36+
'logs-*',
37+
'packetbeat-*',
38+
'winlogbeat-*',
39+
'.siem-signals-angelachuang-default',
40+
],
41+
description: 'hhh',
42+
templateTimelineVersion: null,
43+
eventType: 'all',
44+
filters: [],
45+
sort: { sortDirection: 'desc', columnId: '@timestamp' },
46+
title: 'title',
47+
kqlMode: 'filter',
48+
timelineType: TimelineType.default,
49+
savedQueryId: null,
50+
kqlQuery: { filterQuery: null },
51+
dataProviders: [],
52+
templateTimelineId: null,
53+
eventNotes: [],
54+
globalNotes: [
55+
{
56+
noteId: '7ba7a520-03f4-11eb-9d9d-ffba20fabba8',
57+
version: 'WzQ0ODEsMV0=',
58+
note: '789',
59+
timelineId: '7af80430-03f4-11eb-9d9d-ffba20fabba8',
60+
created: 1601563414477,
61+
createdBy: 'Elastic',
62+
updated: 1601563414477,
63+
updatedBy: 'Elastic',
64+
},
65+
],
66+
pinnedEventIds: [],
67+
});
68+
69+
beforeAll(() => {
70+
Date = (jest.fn(() => ({
71+
valueOf: jest.fn().mockReturnValue(mockDateNow),
72+
})) as unknown) as DateConstructor;
73+
});
74+
75+
afterAll(() => {
76+
((Date as unknown) as jest.Mock).mockRestore();
77+
});
78+
79+
describe('Set create / update time correctly ', () => {
80+
test('Creating a timeline', () => {
81+
const savedTimeline = getMockSavedTimeline();
82+
const timelineId = null;
83+
const userInfo = { username: 'elastic' } as AuthenticatedUser;
84+
const result = pickSavedTimeline(timelineId, savedTimeline, userInfo);
85+
86+
expect(result.created).toEqual(mockDateNow);
87+
expect(result.updated).toEqual(mockDateNow);
88+
});
89+
90+
test('Updating a timeline', () => {
91+
const savedTimeline = getMockSavedTimeline();
92+
const timelineId = savedTimeline.savedObjectId;
93+
const userInfo = { username: 'elastic' } as AuthenticatedUser;
94+
const result = pickSavedTimeline(timelineId, savedTimeline, userInfo);
95+
96+
expect(result.created).toEqual(savedTimeline.created);
97+
expect(result.updated).toEqual(mockDateNow);
98+
});
99+
});
100+
101+
describe('Set userInfo correctly ', () => {
102+
test('Creating a timeline', () => {
103+
const savedTimeline = getMockSavedTimeline();
104+
const timelineId = null;
105+
const userInfo = { username: 'elastic' } as AuthenticatedUser;
106+
const result = pickSavedTimeline(timelineId, savedTimeline, userInfo);
107+
108+
expect(result.createdBy).toEqual(userInfo.username);
109+
expect(result.updatedBy).toEqual(userInfo.username);
110+
});
111+
112+
test('Updating a timeline', () => {
113+
const savedTimeline = getMockSavedTimeline();
114+
const timelineId = savedTimeline.savedObjectId;
115+
const userInfo = { username: 'elastic' } as AuthenticatedUser;
116+
const result = pickSavedTimeline(timelineId, savedTimeline, userInfo);
117+
118+
expect(result.createdBy).toEqual(savedTimeline.createdBy);
119+
expect(result.updatedBy).toEqual(userInfo.username);
120+
});
121+
});
122+
123+
describe('Set status correctly ', () => {
124+
test('Creating a timeline with title', () => {
125+
const savedTimeline = getMockSavedTimeline();
126+
const timelineId = null;
127+
const userInfo = { username: 'elastic' } as AuthenticatedUser;
128+
const result = pickSavedTimeline(timelineId, savedTimeline, userInfo);
129+
130+
expect(result.status).toEqual(TimelineStatus.active);
131+
});
132+
133+
test('Creating a timeline without title', () => {
134+
const savedTimeline = { ...getMockSavedTimeline(), title: null };
135+
const timelineId = null;
136+
const userInfo = { username: 'elastic' } as AuthenticatedUser;
137+
const result = pickSavedTimeline(timelineId, savedTimeline, userInfo);
138+
139+
expect(result.status).toEqual(TimelineStatus.draft);
140+
});
141+
142+
test('Updating a timeline with a new title', () => {
143+
const savedTimeline = getMockSavedTimeline();
144+
const timelineId = savedTimeline.savedObjectId;
145+
const userInfo = { username: 'elastic' } as AuthenticatedUser;
146+
const result = pickSavedTimeline(timelineId, savedTimeline, userInfo);
147+
148+
expect(result.status).toEqual(TimelineStatus.active);
149+
});
150+
151+
test('Updating a timeline without title', () => {
152+
const savedTimeline = getMockSavedTimeline();
153+
const timelineId = savedTimeline.savedObjectId;
154+
const userInfo = { username: 'elastic' } as AuthenticatedUser;
155+
const result = pickSavedTimeline(timelineId, savedTimeline, userInfo);
156+
157+
expect(result.status).toEqual(TimelineStatus.active);
158+
});
159+
160+
test('Updating an immutable timeline with a new title', () => {
161+
const savedTimeline = { ...getMockSavedTimeline(), status: TimelineStatus.immutable };
162+
const timelineId = savedTimeline.savedObjectId;
163+
const userInfo = { username: 'elastic' } as AuthenticatedUser;
164+
const result = pickSavedTimeline(timelineId, savedTimeline, userInfo);
165+
166+
expect(result.status).toEqual(TimelineStatus.immutable);
167+
});
168+
169+
test('Creating a draft timeline with title', () => {
170+
const savedTimeline = { ...getMockSavedTimeline(), status: TimelineStatus.draft };
171+
const timelineId = null;
172+
const userInfo = { username: 'elastic' } as AuthenticatedUser;
173+
const result = pickSavedTimeline(timelineId, savedTimeline, userInfo);
174+
175+
expect(result.status).toEqual(TimelineStatus.active);
176+
});
177+
178+
test('Creating a draft timeline without title', () => {
179+
const savedTimeline = {
180+
...getMockSavedTimeline(),
181+
title: null,
182+
status: TimelineStatus.draft,
183+
};
184+
const timelineId = null;
185+
const userInfo = { username: 'elastic' } as AuthenticatedUser;
186+
const result = pickSavedTimeline(timelineId, savedTimeline, userInfo);
187+
188+
expect(result.status).toEqual(TimelineStatus.draft);
189+
});
190+
191+
test('Updating an untitled draft timeline with a title', () => {
192+
const savedTimeline = { ...getMockSavedTimeline(), status: TimelineStatus.draft };
193+
const timelineId = savedTimeline.savedObjectId;
194+
const userInfo = { username: 'elastic' } as AuthenticatedUser;
195+
const result = pickSavedTimeline(timelineId, savedTimeline, userInfo);
196+
197+
expect(result.status).toEqual(TimelineStatus.active);
198+
});
199+
200+
test('Updating a draft timeline with a new title', () => {
201+
const savedTimeline = { ...getMockSavedTimeline(), status: TimelineStatus.draft };
202+
const timelineId = savedTimeline.savedObjectId;
203+
const userInfo = { username: 'elastic' } as AuthenticatedUser;
204+
const result = pickSavedTimeline(timelineId, savedTimeline, userInfo);
205+
206+
expect(result.status).toEqual(TimelineStatus.active);
207+
});
208+
209+
test('Updating a draft timeline without title', () => {
210+
const savedTimeline = { ...getMockSavedTimeline(), status: TimelineStatus.draft };
211+
const timelineId = savedTimeline.savedObjectId;
212+
const userInfo = { username: 'elastic' } as AuthenticatedUser;
213+
const result = pickSavedTimeline(timelineId, savedTimeline, userInfo);
214+
215+
expect(result.status).toEqual(TimelineStatus.active);
216+
});
217+
});
218+
219+
test('Set timelineType correctly ', () => {
220+
const savedTimeline = getMockSavedTimeline();
221+
const timelineId = null;
222+
const userInfo = { username: 'elastic' } as AuthenticatedUser;
223+
const result = pickSavedTimeline(timelineId, savedTimeline, userInfo);
224+
225+
expect(result.timelineType).toEqual(TimelineType.default);
226+
expect(result.status).toEqual(TimelineStatus.active);
227+
expect(result.templateTimelineId).toBeNull();
228+
expect(result.templateTimelineVersion).toBeNull();
229+
});
230+
231+
test('Set excludedRowRendererIds correctly ', () => {
232+
const savedTimeline = getMockSavedTimeline();
233+
const timelineId = null;
234+
const userInfo = { username: 'elastic' } as AuthenticatedUser;
235+
const result = pickSavedTimeline(timelineId, savedTimeline, userInfo);
236+
237+
expect(result.excludedRowRendererIds).toEqual([]);
238+
});
239+
});

x-pack/plugins/security_solution/server/lib/timeline/pick_saved_timeline.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const pickSavedTimeline = (
2727
savedTimeline.updatedBy = userInfo?.username ?? UNAUTHENTICATED_USER;
2828
}
2929

30-
if (savedTimeline.status === TimelineStatus.draft) {
30+
if (savedTimeline.status === TimelineStatus.draft || savedTimeline.status == null) {
3131
savedTimeline.status = !isEmpty(savedTimeline.title)
3232
? TimelineStatus.active
3333
: TimelineStatus.draft;

0 commit comments

Comments
 (0)