diff --git a/docs/api/dashboard-api.asciidoc b/docs/api/dashboard-api.asciidoc index 94511c3154fe0..e6f54dd9156ec 100644 --- a/docs/api/dashboard-api.asciidoc +++ b/docs/api/dashboard-api.asciidoc @@ -1,7 +1,7 @@ [[dashboard-api]] == Import and export dashboard APIs -deprecated::[7.15.0,These experimental APIs have been deprecated in favor of <> and <>.] +deprecated::[7.15.0,Both of these APIs have been deprecated in favor of <> and <>.] Import and export dashboards with the corresponding saved objects, such as visualizations, saved searches, and index patterns. diff --git a/docs/api/dashboard/export-dashboard.asciidoc b/docs/api/dashboard/export-dashboard.asciidoc index 098ec976569bd..3a20eff0a54d2 100644 --- a/docs/api/dashboard/export-dashboard.asciidoc +++ b/docs/api/dashboard/export-dashboard.asciidoc @@ -6,7 +6,7 @@ deprecated::[7.15.0,Use <> instead.] -experimental[] Export dashboards and corresponding saved objects. +Export dashboards and corresponding saved objects. [[dashboard-api-export-request]] ==== Request diff --git a/docs/api/dashboard/import-dashboard.asciidoc b/docs/api/dashboard/import-dashboard.asciidoc index 41eb47500c8d7..e4817d6cb7ee9 100644 --- a/docs/api/dashboard/import-dashboard.asciidoc +++ b/docs/api/dashboard/import-dashboard.asciidoc @@ -6,7 +6,7 @@ deprecated::[7.15.0,Use <> instead.] -experimental[] Import dashboards and corresponding saved objects. +Import dashboards and corresponding saved objects. [[dashboard-api-import-request]] ==== Request diff --git a/packages/kbn-securitysolution-autocomplete/src/field/index.tsx b/packages/kbn-securitysolution-autocomplete/src/field/index.tsx index 69408e919bb1e..a89e0a096b673 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field/index.tsx +++ b/packages/kbn-securitysolution-autocomplete/src/field/index.tsx @@ -8,7 +8,7 @@ import React, { useCallback, useMemo, useState } from 'react'; import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; -import { IndexPatternBase, IndexPatternFieldBase } from '@kbn/es-query'; +import { DataViewBase, DataViewFieldBase } from '@kbn/es-query'; import { getGenericComboBoxProps, @@ -20,14 +20,14 @@ const AS_PLAIN_TEXT = { asPlainText: true }; interface OperatorProps { fieldInputWidth?: number; fieldTypeFilter?: string[]; - indexPattern: IndexPatternBase | undefined; + indexPattern: DataViewBase | undefined; isClearable: boolean; isDisabled: boolean; isLoading: boolean; isRequired?: boolean; - onChange: (a: IndexPatternFieldBase[]) => void; + onChange: (a: DataViewFieldBase[]) => void; placeholder: string; - selectedField: IndexPatternFieldBase | undefined; + selectedField: DataViewFieldBase | undefined; } export const FieldComponent: React.FC = ({ @@ -56,7 +56,7 @@ export const FieldComponent: React.FC = ({ const handleValuesChange = useCallback( (newOptions: EuiComboBoxOptionOption[]): void => { - const newValues: IndexPatternFieldBase[] = newOptions.map( + const newValues: DataViewFieldBase[] = newOptions.map( ({ label }) => availableFields[labels.indexOf(label)] ); onChange(newValues); @@ -94,13 +94,13 @@ export const FieldComponent: React.FC = ({ FieldComponent.displayName = 'Field'; interface ComboBoxFields { - availableFields: IndexPatternFieldBase[]; - selectedFields: IndexPatternFieldBase[]; + availableFields: DataViewFieldBase[]; + selectedFields: DataViewFieldBase[]; } const getComboBoxFields = ( - indexPattern: IndexPatternBase | undefined, - selectedField: IndexPatternFieldBase | undefined, + indexPattern: DataViewBase | undefined, + selectedField: DataViewFieldBase | undefined, fieldTypeFilter: string[] ): ComboBoxFields => { const existingFields = getExistingFields(indexPattern); @@ -113,29 +113,27 @@ const getComboBoxFields = ( const getComboBoxProps = (fields: ComboBoxFields): GetGenericComboBoxPropsReturn => { const { availableFields, selectedFields } = fields; - return getGenericComboBoxProps({ + return getGenericComboBoxProps({ getLabel: (field) => field.name, options: availableFields, selectedOptions: selectedFields, }); }; -const getExistingFields = (indexPattern: IndexPatternBase | undefined): IndexPatternFieldBase[] => { +const getExistingFields = (indexPattern: DataViewBase | undefined): DataViewFieldBase[] => { return indexPattern != null ? indexPattern.fields : []; }; -const getSelectedFields = ( - selectedField: IndexPatternFieldBase | undefined -): IndexPatternFieldBase[] => { +const getSelectedFields = (selectedField: DataViewFieldBase | undefined): DataViewFieldBase[] => { return selectedField ? [selectedField] : []; }; const getAvailableFields = ( - existingFields: IndexPatternFieldBase[], - selectedFields: IndexPatternFieldBase[], + existingFields: DataViewFieldBase[], + selectedFields: DataViewFieldBase[], fieldTypeFilter: string[] -): IndexPatternFieldBase[] => { - const fieldsByName = new Map(); +): DataViewFieldBase[] => { + const fieldsByName = new Map(); existingFields.forEach((f) => fieldsByName.set(f.name, f)); selectedFields.forEach((f) => fieldsByName.set(f.name, f)); diff --git a/src/plugins/data_views/server/fetcher/index_patterns_fetcher.test.ts b/src/plugins/data_views/server/fetcher/index_patterns_fetcher.test.ts index a65d4d551cf7c..1a8b705480258 100644 --- a/src/plugins/data_views/server/fetcher/index_patterns_fetcher.test.ts +++ b/src/plugins/data_views/server/fetcher/index_patterns_fetcher.test.ts @@ -5,7 +5,6 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - import { IndexPatternsFetcher } from '.'; import { ElasticsearchClient } from 'kibana/server'; import * as indexNotFoundException from './index_not_found_exception.json'; @@ -15,36 +14,36 @@ describe('Index Pattern Fetcher - server', () => { let esClient: ElasticsearchClient; const emptyResponse = { body: { - count: 0, + indices: [], }, }; const response = { body: { - count: 1115, + indices: ['b'], + fields: [{ name: 'foo' }, { name: 'bar' }, { name: 'baz' }], }, }; const patternList = ['a', 'b', 'c']; beforeEach(() => { + jest.clearAllMocks(); esClient = { - count: jest.fn().mockResolvedValueOnce(emptyResponse).mockResolvedValue(response), + fieldCaps: jest.fn().mockResolvedValueOnce(emptyResponse).mockResolvedValue(response), } as unknown as ElasticsearchClient; indexPatterns = new IndexPatternsFetcher(esClient); }); - it('Removes pattern without matching indices', async () => { const result = await indexPatterns.validatePatternListActive(patternList); expect(result).toEqual(['b', 'c']); }); - it('Returns all patterns when all match indices', async () => { esClient = { - count: jest.fn().mockResolvedValue(response), + fieldCaps: jest.fn().mockResolvedValue(response), } as unknown as ElasticsearchClient; indexPatterns = new IndexPatternsFetcher(esClient); const result = await indexPatterns.validatePatternListActive(patternList); expect(result).toEqual(patternList); }); - it('Removes pattern when "index_not_found_exception" error is thrown', async () => { + it('Removes pattern when error is thrown', async () => { class ServerError extends Error { public body?: Record; constructor( @@ -56,9 +55,8 @@ describe('Index Pattern Fetcher - server', () => { this.body = errBody; } } - esClient = { - count: jest + fieldCaps: jest .fn() .mockResolvedValueOnce(response) .mockRejectedValue( @@ -69,4 +67,22 @@ describe('Index Pattern Fetcher - server', () => { const result = await indexPatterns.validatePatternListActive(patternList); expect(result).toEqual([patternList[0]]); }); + it('When allowNoIndices is false, run validatePatternListActive', async () => { + const fieldCapsMock = jest.fn(); + esClient = { + fieldCaps: fieldCapsMock.mockResolvedValue(response), + } as unknown as ElasticsearchClient; + indexPatterns = new IndexPatternsFetcher(esClient); + await indexPatterns.getFieldsForWildcard({ pattern: patternList }); + expect(fieldCapsMock.mock.calls).toHaveLength(4); + }); + it('When allowNoIndices is true, do not run validatePatternListActive', async () => { + const fieldCapsMock = jest.fn(); + esClient = { + fieldCaps: fieldCapsMock.mockResolvedValue(response), + } as unknown as ElasticsearchClient; + indexPatterns = new IndexPatternsFetcher(esClient, true); + await indexPatterns.getFieldsForWildcard({ pattern: patternList }); + expect(fieldCapsMock.mock.calls).toHaveLength(1); + }); }); diff --git a/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts b/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts index 7dae85c920ebf..c054d547e956f 100644 --- a/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts +++ b/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts @@ -36,12 +36,10 @@ interface FieldSubType { export class IndexPatternsFetcher { private elasticsearchClient: ElasticsearchClient; private allowNoIndices: boolean; - constructor(elasticsearchClient: ElasticsearchClient, allowNoIndices: boolean = false) { this.elasticsearchClient = elasticsearchClient; this.allowNoIndices = allowNoIndices; } - /** * Get a list of field objects for an index pattern that may contain wildcards * @@ -60,23 +58,22 @@ export class IndexPatternsFetcher { }): Promise { const { pattern, metaFields, fieldCapsOptions, type, rollupIndex } = options; const patternList = Array.isArray(pattern) ? pattern : pattern.split(','); + const allowNoIndices = fieldCapsOptions + ? fieldCapsOptions.allow_no_indices + : this.allowNoIndices; let patternListActive: string[] = patternList; // if only one pattern, don't bother with validation. We let getFieldCapabilities fail if the single pattern is bad regardless - if (patternList.length > 1) { + if (patternList.length > 1 && !allowNoIndices) { patternListActive = await this.validatePatternListActive(patternList); } const fieldCapsResponse = await getFieldCapabilities( this.elasticsearchClient, - // if none of the patterns are active, pass the original list to get an error - patternListActive.length > 0 ? patternListActive : patternList, + patternListActive, metaFields, { - allow_no_indices: fieldCapsOptions - ? fieldCapsOptions.allow_no_indices - : this.allowNoIndices, + allow_no_indices: allowNoIndices, } ); - if (type === 'rollup' && rollupIndex) { const rollupFields: FieldDescriptor[] = []; const rollupIndexCapabilities = getCapabilitiesForRollupIndices( @@ -87,13 +84,11 @@ export class IndexPatternsFetcher { ).body )[rollupIndex].aggs; const fieldCapsResponseObj = keyBy(fieldCapsResponse, 'name'); - // Keep meta fields metaFields!.forEach( (field: string) => fieldCapsResponseObj[field] && rollupFields.push(fieldCapsResponseObj[field]) ); - return mergeCapabilitiesWithFields( rollupIndexCapabilities, fieldCapsResponseObj, @@ -137,23 +132,20 @@ export class IndexPatternsFetcher { async validatePatternListActive(patternList: string[]) { const result = await Promise.all( patternList - .map((pattern) => - this.elasticsearchClient.count({ - index: pattern, - }) - ) - .map((p) => - p.catch((e) => { - if (e.body.error.type === 'index_not_found_exception') { - return { body: { count: 0 } }; - } - throw e; - }) - ) + .map(async (index) => { + const searchResponse = await this.elasticsearchClient.fieldCaps({ + index, + fields: '_id', + ignore_unavailable: true, + allow_no_indices: false, + }); + return searchResponse.body.indices.length > 0; + }) + .map((p) => p.catch(() => false)) ); return result.reduce( - (acc: string[], { body: { count } }, patternListIndex) => - count > 0 ? [...acc, patternList[patternListIndex]] : acc, + (acc: string[], isValid, patternListIndex) => + isValid ? [...acc, patternList[patternListIndex]] : acc, [] ); } diff --git a/x-pack/plugins/observability/public/pages/alerts/alerts_table_t_grid.tsx b/x-pack/plugins/observability/public/pages/alerts/alerts_table_t_grid.tsx index ace01aa851ce8..13ef1c7725a3d 100644 --- a/x-pack/plugins/observability/public/pages/alerts/alerts_table_t_grid.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/alerts_table_t_grid.tsx @@ -412,6 +412,8 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) { }, renderCellValue: getRenderCellValue({ setFlyoutAlert }), rowRenderers: NO_ROW_RENDER, + // TODO: implement Kibana data view runtime fields in observability + runtimeMappings: {}, start: rangeFrom, setRefetch, sort: [ diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 2772c3de51065..584f3ed334d89 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -11,8 +11,16 @@ import type { TransformConfigSchema } from './transforms/types'; import { ENABLE_CASE_CONNECTOR } from '../../cases/common'; import { METADATA_TRANSFORMS_PATTERN } from './endpoint/constants'; +/** + * as const + * + * The const assertion ensures that type widening does not occur + * https://mariusschulz.com/blog/literal-type-widening-in-typescript + * Please follow this convention when adding to this file + */ + export const APP_ID = 'securitySolution' as const; -export const APP_UI_ID = 'securitySolutionUI'; +export const APP_UI_ID = 'securitySolutionUI' as const; export const CASES_FEATURE_ID = 'securitySolutionCases' as const; export const SERVER_APP_ID = 'siem' as const; export const APP_NAME = 'Security' as const; @@ -26,6 +34,8 @@ export const DEFAULT_DATE_FORMAT_TZ = 'dateFormat:tz' as const; export const DEFAULT_DARK_MODE = 'theme:darkMode' as const; export const DEFAULT_INDEX_KEY = 'securitySolution:defaultIndex' as const; export const DEFAULT_NUMBER_FORMAT = 'format:number:defaultPattern' as const; +export const DEFAULT_DATA_VIEW_ID = 'security-solution' as const; +export const DEFAULT_TIME_FIELD = '@timestamp' as const; export const DEFAULT_TIME_RANGE = 'timepicker:timeDefaults' as const; export const DEFAULT_REFRESH_RATE_INTERVAL = 'timepicker:refreshIntervalDefaults' as const; export const DEFAULT_APP_TIME_RANGE = 'securitySolution:timeDefaults' as const; @@ -51,7 +61,6 @@ export const DEFAULT_TIMEPICKER_QUICK_RANGES = 'timepicker:quickRanges' as const export const DEFAULT_TRANSFORMS = 'securitySolution:transforms' as const; export const SCROLLING_DISABLED_CLASS_NAME = 'scrolling-disabled' as const; export const GLOBAL_HEADER_HEIGHT = 96 as const; // px -export const GLOBAL_HEADER_HEIGHT_WITH_GLOBAL_BANNER = 128 as const; // px export const FILTERS_GLOBAL_HEIGHT = 109 as const; // px export const FULL_SCREEN_TOGGLED_CLASS_NAME = 'fullScreenToggled' as const; export const NO_ALERT_INDEX = 'no-alert-index-049FC71A-4C2C-446F-9901-37XMC5024C51' as const; @@ -268,6 +277,7 @@ export const TIMELINE_PREPACKAGED_URL = `${TIMELINE_URL}/_prepackaged` as const; export const NOTE_URL = '/api/note' as const; export const PINNED_EVENT_URL = '/api/pinned_event' as const; +export const SOURCERER_API_URL = '/api/sourcerer' as const; /** * Default signals index key for kibana.dev.yml @@ -355,7 +365,7 @@ export const ELASTIC_NAME = 'estc' as const; export const METADATA_TRANSFORM_STATS_URL = `/api/transform/transforms/${METADATA_TRANSFORMS_PATTERN}/_stats`; -export const RISKY_HOSTS_INDEX_PREFIX = 'ml_host_risk_score_latest_'; +export const RISKY_HOSTS_INDEX_PREFIX = 'ml_host_risk_score_latest_' as const; export const TRANSFORM_STATES = { ABORTING: 'aborting', diff --git a/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts b/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts index 033e979d2814c..42c10614975eb 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts @@ -11,7 +11,7 @@ import type { CreateExceptionListItemSchema, } from '@kbn/securitysolution-io-ts-list-types'; import { buildExceptionFilter } from '@kbn/securitysolution-list-utils'; -import { Filter, EsQueryConfig, IndexPatternBase, buildEsQuery } from '@kbn/es-query'; +import { Filter, EsQueryConfig, DataViewBase, buildEsQuery } from '@kbn/es-query'; import { ESBoolQuery } from '../typed_json'; import { Query, Index, TimestampOverrideOrUndefined } from './schemas/common/schemas'; @@ -24,7 +24,7 @@ export const getQueryFilter = ( lists: Array, excludeExceptions: boolean = true ): ESBoolQuery => { - const indexPattern: IndexPatternBase = { + const indexPattern: DataViewBase = { fields: [], title: index.join(), }; diff --git a/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts b/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts index be5fd3b5c4dc5..35fd5e81b096f 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts @@ -5,79 +5,15 @@ * 2.0. */ -import type { IFieldSubType } from '@kbn/es-query'; - -import type { - IEsSearchRequest, - IEsSearchResponse, - IIndexPattern, -} from '../../../../../../src/plugins/data/common'; -import type { DocValueFields, Maybe } from '../common'; - -interface FieldInfo { - category: string; - description?: string; - example?: string | number; - format?: string; - name: string; - type?: string; -} - -export interface IndexField { - /** Where the field belong */ - category: string; - /** Example of field's value */ - example?: Maybe; - /** whether the field's belong to an alias index */ - indexes: Array>; - /** The name of the field */ - name: string; - /** The type of the field's values as recognized by Kibana */ - type: string; - /** Whether the field's values can be efficiently searched for */ - searchable: boolean; - /** Whether the field's values can be aggregated */ - aggregatable: boolean; - /** Description of the field */ - description?: Maybe; - format?: Maybe; - /** the elastic type as mapped in the index */ - esTypes?: string[]; - subType?: IFieldSubType; - readFromDocValues: boolean; -} - -export type BeatFields = Record; - -export interface IndexFieldsStrategyRequest extends IEsSearchRequest { - indices: string[]; - onlyCheckIfIndicesExist: boolean; -} - -export interface IndexFieldsStrategyResponse extends IEsSearchResponse { - indexFields: IndexField[]; - indicesExist: string[]; -} - -export interface BrowserField { - aggregatable: boolean; - category: string; - description: string | null; - example: string | number | null; - fields: Readonly>>; - format: string; - indexes: string[]; - name: string; - searchable: boolean; - type: string; - subType?: IFieldSubType; -} - -export type BrowserFields = Readonly>>; - -export const EMPTY_BROWSER_FIELDS = {}; -export const EMPTY_DOCVALUE_FIELD: DocValueFields[] = []; -export const EMPTY_INDEX_PATTERN: IIndexPattern = { - fields: [], - title: '', -}; +export { + FieldInfo, + IndexField, + BeatFields, + IndexFieldsStrategyRequest, + IndexFieldsStrategyResponse, + BrowserField, + BrowserFields, + EMPTY_BROWSER_FIELDS, + EMPTY_DOCVALUE_FIELD, + EMPTY_INDEX_FIELDS, +} from '../../../../timelines/common'; diff --git a/x-pack/plugins/security_solution/common/search_strategy/timeline/events/last_event_time/index.ts b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/last_event_time/index.ts index 39f23a63c8afe..d6735b59c229d 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/timeline/events/last_event_time/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/last_event_time/index.ts @@ -10,6 +10,7 @@ export { LastEventIndexKey } from '../../../../../../timelines/common'; export type { LastTimeDetails, TimelineEventsLastEventTimeStrategyResponse, + TimelineKpiStrategyRequest, TimelineKpiStrategyResponse, TimelineEventsLastEventTimeRequestOptions, } from '../../../../../../timelines/common'; diff --git a/x-pack/plugins/security_solution/common/search_strategy/timeline/index.ts b/x-pack/plugins/security_solution/common/search_strategy/timeline/index.ts index 548560ac5cb8c..2d94a36a937d5 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/timeline/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/timeline/index.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { IEsSearchRequest } from '../../../../../../src/plugins/data/common'; import { ESQuery } from '../../typed_json'; import { @@ -41,6 +42,7 @@ export interface TimelineRequestBasicOptions extends IEsSearchRequest { defaultIndex: string[]; docValueFields?: DocValueFields[]; factoryQueryType?: TimelineFactoryQueryTypes; + runtimeMappings: MappingRuntimeFields; } export interface TimelineRequestSortField extends SortField { @@ -171,6 +173,7 @@ export interface SortTimelineInput { export interface TimelineInput { columns?: Maybe; dataProviders?: Maybe; + dataViewId?: Maybe; description?: Maybe; eqlOptions?: Maybe; eventType?: Maybe; diff --git a/x-pack/plugins/security_solution/common/test/index.ts b/x-pack/plugins/security_solution/common/test/index.ts index 6d5df76b306a3..53261d54e84b0 100644 --- a/x-pack/plugins/security_solution/common/test/index.ts +++ b/x-pack/plugins/security_solution/common/test/index.ts @@ -7,12 +7,12 @@ // For the source of these roles please consult the PR these were introduced https://github.com/elastic/kibana/pull/81866#issue-511165754 export enum ROLES { + soc_manager = 'soc_manager', reader = 'reader', t1_analyst = 't1_analyst', t2_analyst = 't2_analyst', hunter = 'hunter', rule_author = 'rule_author', - soc_manager = 'soc_manager', platform_engineer = 'platform_engineer', detections_admin = 'detections_admin', } diff --git a/x-pack/plugins/security_solution/common/types/timeline/index.ts b/x-pack/plugins/security_solution/common/types/timeline/index.ts index c0046f7535db8..60fd126e6fd85 100644 --- a/x-pack/plugins/security_solution/common/types/timeline/index.ts +++ b/x-pack/plugins/security_solution/common/types/timeline/index.ts @@ -272,6 +272,7 @@ export type TimelineTypeLiteralWithNull = runtimeTypes.TypeOf; +export type TimelineWithoutExternalRefs = Omit; /* * Timeline IDs @@ -719,6 +720,7 @@ export interface TimelineResult { created?: Maybe; createdBy?: Maybe; dataProviders?: Maybe; + dataViewId?: Maybe; dateRange?: Maybe; description?: Maybe; eqlOptions?: Maybe; diff --git a/x-pack/plugins/security_solution/common/types/timeline/store.ts b/x-pack/plugins/security_solution/common/types/timeline/store.ts index 03cf0c39378e5..75cd44ba2b7d7 100644 --- a/x-pack/plugins/security_solution/common/types/timeline/store.ts +++ b/x-pack/plugins/security_solution/common/types/timeline/store.ts @@ -38,19 +38,20 @@ export interface SortColumnTimeline { } export interface TimelinePersistInput { - id: string; + columns: ColumnHeaderOptions[]; dataProviders?: DataProvider[]; + dataViewId: string; dateRange?: { start: string; end: string; }; + defaultColumns?: ColumnHeaderOptions[]; excludedRowRendererIds?: RowRendererId[]; expandedDetail?: TimelineExpandedDetail; filters?: Filter[]; - columns: ColumnHeaderOptions[]; - defaultColumns?: ColumnHeaderOptions[]; - itemsPerPage?: number; + id: string; indexNames: string[]; + itemsPerPage?: number; kqlQuery?: { filterQuery: SerializedFilterQuery | null; }; diff --git a/x-pack/plugins/security_solution/cypress/cypress.json b/x-pack/plugins/security_solution/cypress/cypress.json index 6a9a240af5873..8c27309becf08 100644 --- a/x-pack/plugins/security_solution/cypress/cypress.json +++ b/x-pack/plugins/security_solution/cypress/cypress.json @@ -12,5 +12,10 @@ "video": false, "videosFolder": "../../../target/kibana-security-solution/cypress/videos", "viewportHeight": 900, - "viewportWidth": 1440 + "viewportWidth": 1440, + "env": { + "protocol": "http", + "hostname": "localhost", + "configport": "5601" + } } diff --git a/x-pack/plugins/security_solution/cypress/downloads/timelines_export.ndjson b/x-pack/plugins/security_solution/cypress/downloads/timelines_export.ndjson new file mode 100644 index 0000000000000..8cf76734ad876 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/downloads/timelines_export.ndjson @@ -0,0 +1 @@ +{"savedObjectId":"46cca0e0-2580-11ec-8e56-9dafa0b0343b","version":"WzIyNjIzNCwxXQ==","columns":[{"id":"@timestamp"},{"id":"user.name"},{"id":"event.category"},{"id":"event.action"},{"id":"host.name"}],"kqlMode":"filter","kqlQuery":{"filterQuery":{"kuery":{"expression":"host.name: *","kind":"kuery"}}},"dateRange":{"start":"1514809376000","end":"1577881376000"},"description":"This is the best timeline","title":"Security Timeline","created":1633399341550,"createdBy":"elastic","updated":1633399341550,"updatedBy":"elastic","savedQueryId":null,"dataViewId":null,"timelineType":"default","sort":[],"eventNotes":[],"globalNotes":[],"pinnedEventIds":[]} diff --git a/x-pack/plugins/security_solution/cypress/integration/cases/privileges.spec.ts b/x-pack/plugins/security_solution/cypress/integration/cases/privileges.spec.ts index 23016ecc512b1..0337cd3bd6e17 100644 --- a/x-pack/plugins/security_solution/cypress/integration/cases/privileges.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/cases/privileges.spec.ts @@ -18,184 +18,28 @@ import { filterStatusOpen, } from '../../tasks/create_new_case'; import { - constructUrlWithUser, - getEnvAuth, + loginAndWaitForHostDetailsPage, loginWithUserAndWaitForPageWithoutDateRange, + logout, } from '../../tasks/login'; +import { + createUsersAndRoles, + deleteUsersAndRoles, + secAll, + secAllUser, + secReadCasesAllUser, + secReadCasesAll, +} from '../../tasks/privileges'; import { CASES_URL } from '../../urls/navigation'; - -interface User { - username: string; - password: string; - description?: string; - roles: string[]; -} - -interface UserInfo { - username: string; - full_name: string; - email: string; -} - -interface FeaturesPrivileges { - [featureId: string]: string[]; -} - -interface ElasticsearchIndices { - names: string[]; - privileges: string[]; -} - -interface ElasticSearchPrivilege { - cluster?: string[]; - indices?: ElasticsearchIndices[]; -} - -interface KibanaPrivilege { - spaces: string[]; - base?: string[]; - feature?: FeaturesPrivileges; -} - -interface Role { - name: string; - privileges: { - elasticsearch?: ElasticSearchPrivilege; - kibana?: KibanaPrivilege[]; - }; -} - -const secAll: Role = { - name: 'sec_all_role', - privileges: { - elasticsearch: { - indices: [ - { - names: ['*'], - privileges: ['all'], - }, - ], - }, - kibana: [ - { - feature: { - siem: ['all'], - securitySolutionCases: ['all'], - actions: ['all'], - actionsSimulators: ['all'], - }, - spaces: ['*'], - }, - ], - }, -}; - -const secAllUser: User = { - username: 'sec_all_user', - password: 'password', - roles: [secAll.name], -}; - -const secReadCasesAll: Role = { - name: 'sec_read_cases_all_role', - privileges: { - elasticsearch: { - indices: [ - { - names: ['*'], - privileges: ['all'], - }, - ], - }, - kibana: [ - { - feature: { - siem: ['read'], - securitySolutionCases: ['all'], - actions: ['all'], - actionsSimulators: ['all'], - }, - spaces: ['*'], - }, - ], - }, -}; - -const secReadCasesAllUser: User = { - username: 'sec_read_cases_all_user', - password: 'password', - roles: [secReadCasesAll.name], -}; - +import { openSourcerer } from '../../tasks/sourcerer'; const usersToCreate = [secAllUser, secReadCasesAllUser]; const rolesToCreate = [secAll, secReadCasesAll]; - -const getUserInfo = (user: User): UserInfo => ({ - username: user.username, - full_name: user.username.replace('_', ' '), - email: `${user.username}@elastic.co`, -}); - -const createUsersAndRoles = (users: User[], roles: Role[]) => { - const envUser = getEnvAuth(); - for (const role of roles) { - cy.log(`Creating role: ${JSON.stringify(role)}`); - cy.request({ - body: role.privileges, - headers: { 'kbn-xsrf': 'cypress-creds-via-config' }, - method: 'PUT', - url: constructUrlWithUser(envUser, `/api/security/role/${role.name}`), - }) - .its('status') - .should('eql', 204); - } - - for (const user of users) { - const userInfo = getUserInfo(user); - cy.log(`Creating user: ${JSON.stringify(user)}`); - cy.request({ - body: { - username: user.username, - password: user.password, - roles: user.roles, - full_name: userInfo.full_name, - email: userInfo.email, - }, - headers: { 'kbn-xsrf': 'cypress-creds-via-config' }, - method: 'POST', - url: constructUrlWithUser(envUser, `/internal/security/users/${user.username}`), - }) - .its('status') - .should('eql', 200); - } -}; - -const deleteUsersAndRoles = (users: User[], roles: Role[]) => { - const envUser = getEnvAuth(); - for (const user of users) { - cy.log(`Deleting user: ${JSON.stringify(user)}`); - cy.request({ - headers: { 'kbn-xsrf': 'cypress-creds-via-config' }, - method: 'DELETE', - url: constructUrlWithUser(envUser, `/internal/security/users/${user.username}`), - failOnStatusCode: false, - }) - .its('status') - .should('oneOf', [204, 404]); - } - - for (const role of roles) { - cy.log(`Deleting role: ${JSON.stringify(role)}`); - cy.request({ - headers: { 'kbn-xsrf': 'cypress-creds-via-config' }, - method: 'DELETE', - url: constructUrlWithUser(envUser, `/api/security/role/${role.name}`), - failOnStatusCode: false, - }) - .its('status') - .should('oneOf', [204, 404]); - } +// needed to generate index pattern +const visitSecuritySolution = () => { + loginAndWaitForHostDetailsPage(); + openSourcerer(); + logout(); }; const testCase: TestCaseWithoutTimeline = { @@ -205,11 +49,11 @@ const testCase: TestCaseWithoutTimeline = { reporter: 'elastic', owner: 'securitySolution', }; - describe('Cases privileges', () => { before(() => { cleanKibana(); createUsersAndRoles(usersToCreate, rolesToCreate); + visitSecuritySolution(); }); after(() => { diff --git a/x-pack/plugins/security_solution/cypress/integration/data_sources/sourcerer.spec.ts b/x-pack/plugins/security_solution/cypress/integration/data_sources/sourcerer.spec.ts index 26c366e981d44..bd7acc38c1021 100644 --- a/x-pack/plugins/security_solution/cypress/integration/data_sources/sourcerer.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/data_sources/sourcerer.spec.ts @@ -5,7 +5,10 @@ * 2.0. */ -import { loginAndWaitForPage } from '../../tasks/login'; +import { + loginAndWaitForPage, + loginWithUserAndWaitForPageWithoutDateRange, +} from '../../tasks/login'; import { HOSTS_URL } from '../../urls/navigation'; import { waitForAllHostsToBeLoaded } from '../../tasks/hosts/all_hosts'; @@ -28,20 +31,34 @@ import { openTimelineUsingToggle } from '../../tasks/security_main'; import { populateTimeline } from '../../tasks/timeline'; import { SERVER_SIDE_EVENT_COUNT } from '../../screens/timeline'; import { cleanKibana } from '../../tasks/common'; +import { createUsersAndRoles, secReadCasesAll, secReadCasesAllUser } from '../../tasks/privileges'; +import { TOASTER } from '../../screens/configure_cases'; +const usersToCreate = [secReadCasesAllUser]; +const rolesToCreate = [secReadCasesAll]; // Skipped at the moment as this has flake due to click handler issues. This has been raised with team members // and the code is being re-worked and then these tests will be unskipped -describe.skip('Sourcerer', () => { - before(() => { +describe('Sourcerer', () => { + beforeEach(() => { cleanKibana(); }); - - beforeEach(() => { - cy.clearLocalStorage(); - loginAndWaitForPage(HOSTS_URL); + describe('permissions', () => { + before(() => { + createUsersAndRoles(usersToCreate, rolesToCreate); + }); + it(`role(s) ${secReadCasesAllUser.roles.join()} shows error when user does not have permissions`, () => { + loginWithUserAndWaitForPageWithoutDateRange(HOSTS_URL, secReadCasesAllUser); + cy.get(TOASTER).should('have.text', 'Write role required to generate data'); + }); }); + // Originially written in December 2020, flakey from day1 + // has always been skipped with intentions to fix, see note at top of file + describe.skip('Default scope', () => { + beforeEach(() => { + cy.clearLocalStorage(); + loginAndWaitForPage(HOSTS_URL); + }); - describe('Default scope', () => { it('has SIEM index patterns selected on initial load', () => { openSourcerer(); isSourcererSelection(`auditbeat-*`); @@ -52,7 +69,7 @@ describe.skip('Sourcerer', () => { isSourcererOptions([`metrics-*`, `logs-*`]); }); - it('selected KIP gets added to sourcerer', () => { + it('selected DATA_VIEW gets added to sourcerer', () => { setSourcererOption(`metrics-*`); openSourcerer(); isSourcererSelection(`metrics-*`); @@ -75,8 +92,14 @@ describe.skip('Sourcerer', () => { isNotSourcererSelection(`metrics-*`); }); }); + // Originially written in December 2020, flakey from day1 + // has always been skipped with intentions to fix + describe.skip('Timeline scope', () => { + beforeEach(() => { + cy.clearLocalStorage(); + loginAndWaitForPage(HOSTS_URL); + }); - describe('Timeline scope', () => { const alertPatterns = ['.siem-signals-default']; const rawPatterns = ['auditbeat-*']; const allPatterns = [...alertPatterns, ...rawPatterns]; diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts index 803ff4b4d0d80..033a12dd9de3e 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts @@ -9,6 +9,7 @@ import { ALERT_FLYOUT, CELL_TEXT, JSON_TEXT, TABLE_ROWS } from '../../screens/al import { expandFirstAlert, + refreshAlerts, waitForAlertsIndexToBeCreated, waitForAlertsPanelToBeLoaded, } from '../../tasks/alerts'; @@ -32,6 +33,7 @@ describe('Alert details with unmapped fields', () => { createCustomRuleActivated(getUnmappedRule()); loginAndWaitForPageWithoutDateRange(ALERTS_URL); waitForAlertsPanelToBeLoaded(); + refreshAlerts(); expandFirstAlert(); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/event_correlation_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/event_correlation_rule.spec.ts index 10f556a11bf60..171d224cc32d3 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/event_correlation_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/event_correlation_rule.spec.ts @@ -70,7 +70,7 @@ import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login'; import { ALERTS_URL } from '../../urls/navigation'; -describe.skip('Detection rules, EQL', () => { +describe('Detection rules, EQL', () => { const expectedUrls = getEqlRule().referenceUrls.join(''); const expectedFalsePositives = getEqlRule().falsePositivesExamples.join(''); const expectedTags = getEqlRule().tags.join(''); @@ -169,7 +169,7 @@ describe.skip('Detection rules, EQL', () => { }); }); -describe.skip('Detection rules, sequence EQL', () => { +describe('Detection rules, sequence EQL', () => { const expectedNumberOfRules = 1; const expectedNumberOfSequenceAlerts = '1 alert'; diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts index 02621ea49e906..378de8f0bc593 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts @@ -114,7 +114,7 @@ import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login'; import { goBackToAllRulesTable } from '../../tasks/rule_details'; import { ALERTS_URL, RULE_CREATION } from '../../urls/navigation'; -import { DEFAULT_THREAT_MATCH_QUERY } from '../../../common/constants'; +const DEFAULT_THREAT_MATCH_QUERY = '@timestamp >= "now-30d"'; describe('indicator match', () => { describe('Detection rules, Indicator Match', () => { diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/sorting.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/sorting.spec.ts index ef3d3a82d40bd..92f9e8180d50c 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/sorting.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/sorting.spec.ts @@ -34,7 +34,6 @@ import { waitForRuleToChangeStatus, } from '../../tasks/alerts_detection_rules'; import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login'; -import { DEFAULT_RULE_REFRESH_INTERVAL_VALUE } from '../../../common/constants'; import { ALERTS_URL } from '../../urls/navigation'; import { createCustomRule } from '../../tasks/api_calls/rules'; @@ -46,6 +45,8 @@ import { getNewThresholdRule, } from '../../objects/rule'; +const DEFAULT_RULE_REFRESH_INTERVAL_VALUE = 60000; + describe('Alerts detection rules', () => { beforeEach(() => { cleanKibana(); diff --git a/x-pack/plugins/security_solution/cypress/integration/ml/ml_conditional_links.spec.ts b/x-pack/plugins/security_solution/cypress/integration/ml/ml_conditional_links.spec.ts index 89a0d5a660b97..f9d78ba12a5ea 100644 --- a/x-pack/plugins/security_solution/cypress/integration/ml/ml_conditional_links.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/ml/ml_conditional_links.spec.ts @@ -98,7 +98,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkSingleIpNullKqlQuery); cy.url().should( 'include', - 'app/security/network/ip/127.0.0.1/source?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' + 'app/security/network/ip/127.0.0.1/source?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))' ); }); @@ -106,7 +106,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkSingleIpKqlQuery); cy.url().should( 'include', - '/app/security/network/ip/127.0.0.1/source?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' + '/app/security/network/ip/127.0.0.1/source?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))' ); }); @@ -114,7 +114,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkMultipleIpNullKqlQuery); cy.url().should( 'include', - 'app/security/network/flows?query=(language:kuery,query:%27((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' + 'app/security/network/flows?query=(language:kuery,query:%27((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))' ); }); @@ -122,7 +122,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkMultipleIpKqlQuery); cy.url().should( 'include', - '/app/security/network/flows?query=(language:kuery,query:%27((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' + '/app/security/network/flows?query=(language:kuery,query:%27((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))' ); }); @@ -130,15 +130,16 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkNullKqlQuery); cy.url().should( 'include', - '/app/security/network/flows?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' + '/app/security/network/flows?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))' ); }); it('redirects from a $ip$ with a value for the query', () => { loginAndWaitForPageWithoutDateRange(mlNetworkKqlQuery); + cy.url().should( 'include', - '/app/security/network/flows?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' + `/app/security/network/flows?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))` ); }); @@ -146,7 +147,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostSingleHostNullKqlQuery); cy.url().should( 'include', - '/app/security/hosts/siem-windows/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' + '/app/security/hosts/siem-windows/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))' ); }); @@ -154,7 +155,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostSingleHostKqlQueryVariable); cy.url().should( 'include', - '/app/security/hosts/siem-windows/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' + '/app/security/hosts/siem-windows/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))' ); }); @@ -162,7 +163,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostSingleHostKqlQuery); cy.url().should( 'include', - '/app/security/hosts/siem-windows/anomalies?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' + '/app/security/hosts/siem-windows/anomalies?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))' ); }); @@ -170,7 +171,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostMultiHostNullKqlQuery); cy.url().should( 'include', - '/app/security/hosts/anomalies?query=(language:kuery,query:%27(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' + '/app/security/hosts/anomalies?query=(language:kuery,query:%27(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))' ); }); @@ -178,7 +179,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostMultiHostKqlQuery); cy.url().should( 'include', - '/app/security/hosts/anomalies?query=(language:kuery,query:%27(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' + '/app/security/hosts/anomalies?query=(language:kuery,query:%27(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))' ); }); @@ -186,7 +187,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostVariableHostNullKqlQuery); cy.url().should( 'include', - '/app/security/hosts/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' + '/app/security/hosts/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))' ); }); @@ -194,7 +195,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostVariableHostKqlQuery); cy.url().should( 'include', - '/app/security/hosts/anomalies?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' + '/app/security/hosts/anomalies?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))' ); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/creation.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/creation.spec.ts index fb41aec91b6c4..cbff911e5d982 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timelines/creation.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timelines/creation.spec.ts @@ -121,7 +121,6 @@ describe('Create a timeline from a template', () => { loginAndWaitForPageWithoutDateRange(TIMELINE_TEMPLATES_URL); waitForTimelinesPanelToBeLoaded(); }); - it('Should have the same query and open the timeline modal', () => { selectCustomTemplates(); cy.wait('@timeline', { timeout: 100000 }); @@ -132,5 +131,6 @@ describe('Create a timeline from a template', () => { cy.get(TIMELINE_FLYOUT_WRAPPER).should('have.css', 'visibility', 'visible'); cy.get(TIMELINE_DESCRIPTION).should('have.text', getTimeline().description); cy.get(TIMELINE_QUERY).should('have.text', getTimeline().query); + closeTimeline(); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/urls/state.spec.ts b/x-pack/plugins/security_solution/cypress/integration/urls/state.spec.ts index 73eb141f1ce3d..28fe1294e6f01 100644 --- a/x-pack/plugins/security_solution/cypress/integration/urls/state.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/urls/state.spec.ts @@ -182,11 +182,10 @@ describe('url state', () => { loginAndWaitForPageWithoutDateRange(ABSOLUTE_DATE_RANGE.url); kqlSearch('source.ip: "10.142.0.9" {enter}'); navigateFromHeaderTo(HOSTS); - cy.get(NETWORK).should( 'have.attr', 'href', - `/app/security/network?query=(language:kuery,query:'source.ip:%20%2210.142.0.9%22%20')&sourcerer=(default:!(\'auditbeat-*\'))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2019-08-01T20:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2019-08-01T20:33:29.186Z')))` + `/app/security/network?query=(language:kuery,query:'source.ip:%20%2210.142.0.9%22%20')&sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2019-08-01T20:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2019-08-01T20:33:29.186Z')))` ); }); @@ -199,12 +198,12 @@ describe('url state', () => { cy.get(HOSTS).should( 'have.attr', 'href', - `/app/security/hosts?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&sourcerer=(default:!(\'auditbeat-*\'))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` + `/app/security/hosts?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` ); cy.get(NETWORK).should( 'have.attr', 'href', - `/app/security/network?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&sourcerer=(default:!(\'auditbeat-*\'))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` + `/app/security/network?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` ); cy.get(HOSTS_NAMES).first().should('have.text', 'siem-kibana'); @@ -215,21 +214,21 @@ describe('url state', () => { cy.get(ANOMALIES_TAB).should( 'have.attr', 'href', - "/app/security/hosts/siem-kibana/anomalies?sourcerer=(default:!('auditbeat-*'))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))&query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')" + "/app/security/hosts/siem-kibana/anomalies?sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))&query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')" ); cy.get(BREADCRUMBS) .eq(1) .should( 'have.attr', 'href', - `/app/security/hosts?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&sourcerer=(default:!(\'auditbeat-*\'))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` + `/app/security/hosts?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` ); cy.get(BREADCRUMBS) .eq(2) .should( 'have.attr', 'href', - `/app/security/hosts/siem-kibana?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&sourcerer=(default:!(\'auditbeat-*\'))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` + `/app/security/hosts/siem-kibana?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` ); }); diff --git a/x-pack/plugins/security_solution/cypress/objects/timeline.ts b/x-pack/plugins/security_solution/cypress/objects/timeline.ts index f3d9bc1b9ef1a..70b8c1b400d51 100644 --- a/x-pack/plugins/security_solution/cypress/objects/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/objects/timeline.ts @@ -87,6 +87,7 @@ export const expectedExportedTimelineTemplate = ( }, }, }, + dataViewId: timelineTemplateBody.dataViewId, dateRange: { start: timelineTemplateBody.dateRange?.start, end: timelineTemplateBody.dateRange?.end, @@ -127,6 +128,7 @@ export const expectedExportedTimeline = (timelineResponse: Cypress.Response