From 3fc8c7af258293e9c8045ac2fed046582aa84f9f Mon Sep 17 00:00:00 2001 From: Liza Katz Date: Mon, 13 Jul 2020 13:47:12 +0300 Subject: [PATCH] Validate incoming url timerange (#70948) * validate incoming url timerange * adjust discover test * fix tests * stabilize tests * oops Co-authored-by: Elastic Machine --- src/plugins/data/public/public.api.md | 2 +- .../state_sync/connect_to_query_state.ts | 5 +- .../data/public/query/timefilter/index.ts | 1 + .../timefilter/lib/validate_timerange.test.ts | 52 +++++++++++++++++++ .../timefilter/lib/validate_timerange.ts | 28 ++++++++++ test/functional/apps/discover/_discover.js | 24 +++++---- 6 files changed, 98 insertions(+), 14 deletions(-) create mode 100644 src/plugins/data/public/query/timefilter/lib/validate_timerange.test.ts create mode 100644 src/plugins/data/public/query/timefilter/lib/validate_timerange.ts diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 01fcefe27df3ee..b532bacf5df252 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -1991,7 +1991,7 @@ export const UI_SETTINGS: { // src/plugins/data/public/index.ts:393:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:394:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:397:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/query/state_sync/connect_to_query_state.ts:40:60 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/query/state_sync/connect_to_query_state.ts:41:60 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts // src/plugins/data/public/types.ts:52:5 - (ae-forgotten-export) The symbol "createFiltersFromValueClickAction" needs to be exported by the entry point index.d.ts // src/plugins/data/public/types.ts:53:5 - (ae-forgotten-export) The symbol "createFiltersFromRangeSelectAction" needs to be exported by the entry point index.d.ts // src/plugins/data/public/types.ts:61:5 - (ae-forgotten-export) The symbol "IndexPatternSelectProps" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/data/public/query/state_sync/connect_to_query_state.ts b/src/plugins/data/public/query/state_sync/connect_to_query_state.ts index e74497a5053b46..2e62dac87f6efc 100644 --- a/src/plugins/data/public/query/state_sync/connect_to_query_state.ts +++ b/src/plugins/data/public/query/state_sync/connect_to_query_state.ts @@ -24,6 +24,7 @@ import { BaseStateContainer } from '../../../../kibana_utils/public'; import { QuerySetup, QueryStart } from '../query_service'; import { QueryState, QueryStateChange } from './types'; import { FilterStateStore, COMPARE_ALL_OPTIONS, compareFilters } from '../../../common'; +import { validateTimeRange } from '../timefilter'; /** * Helper to setup two-way syncing of global data and a state container @@ -159,9 +160,9 @@ export const connectToQueryState = ( // cloneDeep is required because services are mutating passed objects // and state in state container is frozen if (syncConfig.time) { - const time = state.time || timefilter.getTimeDefaults(); + const time = validateTimeRange(state.time) ? state.time : timefilter.getTimeDefaults(); if (!_.isEqual(time, timefilter.getTime())) { - timefilter.setTime(_.cloneDeep(time)); + timefilter.setTime(_.cloneDeep(time!)); } } diff --git a/src/plugins/data/public/query/timefilter/index.ts b/src/plugins/data/public/query/timefilter/index.ts index f71061677ceb78..19386c10ab59fd 100644 --- a/src/plugins/data/public/query/timefilter/index.ts +++ b/src/plugins/data/public/query/timefilter/index.ts @@ -24,3 +24,4 @@ export { Timefilter, TimefilterContract } from './timefilter'; export { TimeHistory, TimeHistoryContract } from './time_history'; export { changeTimeFilter, convertRangeFilterToTimeRangeString } from './lib/change_time_filter'; export { extractTimeFilter } from './lib/extract_time_filter'; +export { validateTimeRange } from './lib/validate_timerange'; diff --git a/src/plugins/data/public/query/timefilter/lib/validate_timerange.test.ts b/src/plugins/data/public/query/timefilter/lib/validate_timerange.test.ts new file mode 100644 index 00000000000000..e20849c21a7170 --- /dev/null +++ b/src/plugins/data/public/query/timefilter/lib/validate_timerange.test.ts @@ -0,0 +1,52 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { validateTimeRange } from './validate_timerange'; + +describe('Validate timerange', () => { + test('Validate no range', () => { + const ok = validateTimeRange(); + + expect(ok).toBe(false); + }); + test('normal range', () => { + const ok = validateTimeRange({ + to: 'now', + from: 'now-7d', + }); + + expect(ok).toBe(true); + }); + test('bad from time', () => { + const ok = validateTimeRange({ + to: 'nowa', + from: 'now-7d', + }); + + expect(ok).toBe(false); + }); + test('bad to time', () => { + const ok = validateTimeRange({ + to: 'now', + from: 'nowa-7d', + }); + + expect(ok).toBe(false); + }); +}); diff --git a/src/plugins/data/public/query/timefilter/lib/validate_timerange.ts b/src/plugins/data/public/query/timefilter/lib/validate_timerange.ts new file mode 100644 index 00000000000000..f9e4aa0ae1cabd --- /dev/null +++ b/src/plugins/data/public/query/timefilter/lib/validate_timerange.ts @@ -0,0 +1,28 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import dateMath from '@elastic/datemath'; +import { TimeRange } from '../../../../common'; + +export function validateTimeRange(time?: TimeRange): boolean { + if (!time) return false; + const momentDateFrom = dateMath.parse(time.from); + const momentDateTo = dateMath.parse(time.to); + return !!(momentDateFrom && momentDateFrom.isValid() && momentDateTo && momentDateTo.isValid()); +} diff --git a/test/functional/apps/discover/_discover.js b/test/functional/apps/discover/_discover.js index 47741c1ab8a0d5..94a271987ecdf7 100644 --- a/test/functional/apps/discover/_discover.js +++ b/test/functional/apps/discover/_discover.js @@ -254,6 +254,19 @@ export default function ({ getService, getPageObjects }) { }); }); + describe('invalid time range in URL', function () { + it('should get the default timerange', async function () { + const prevTime = await PageObjects.timePicker.getTimeConfig(); + await PageObjects.common.navigateToUrl('discover', '#/?_g=(time:(from:now-15m,to:null))', { + useActualUrl: true, + }); + await PageObjects.header.awaitKibanaChrome(); + const time = await PageObjects.timePicker.getTimeConfig(); + expect(time.start).to.be(prevTime.start); + expect(time.end).to.be(prevTime.end); + }); + }); + describe('empty query', function () { it('should update the histogram timerange when the query is resubmitted', async function () { await kibanaServer.uiSettings.update({ @@ -268,17 +281,6 @@ export default function ({ getService, getPageObjects }) { }); }); - describe('invalid time range in URL', function () { - it('should display a "Invalid time range toast"', async function () { - await PageObjects.common.navigateToUrl('discover', '#/?_g=(time:(from:now-15m,to:null))', { - useActualUrl: true, - }); - await PageObjects.header.awaitKibanaChrome(); - const toastMessage = await PageObjects.common.closeToast(); - expect(toastMessage).to.be('Invalid time range'); - }); - }); - describe('managing fields', function () { it('should add a field, sort by it, remove it and also sorting by it', async function () { await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings();