"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isKibanaStringType = exports.isEntryNested = exports.getUpdatedEntriesOnDelete = exports.getOperatorType = exports.getOperatorOptions = exports.getNewExceptionItem = exports.getMappingConflictsInfo = exports.getFormattedBuilderEntry = exports.getFormattedBuilderEntries = exports.getFilteredIndexPatterns = exports.getExceptionOperatorSelect = exports.getEntryValue = exports.getEntryOnWildcardChange = exports.getEntryOnOperatorChange = exports.getEntryOnMatchChange = exports.getEntryOnMatchAnyChange = exports.getEntryOnListChange = exports.getEntryOnFieldChange = exports.getEntryFromOperator = exports.getDefaultNestedEmptyEntry = exports.getDefaultEmptyEntry = exports.getCorrespondingKeywordField = exports.filterExceptionItems = exports.fieldSupportsMatches = exports.containsValueListEntry = exports.buildShowExpiredExceptionsFilter = exports.buildShowActiveExceptionsFilter = exports.addIdToEntries = void 0; var _uuid = require("uuid"); var _securitysolutionUtils = require("@kbn/securitysolution-utils"); var _securitysolutionIoTsUtils = require("@kbn/securitysolution-io-ts-utils"); var _securitysolutionIoTsListTypes = require("@kbn/securitysolution-io-ts-list-types"); var _esQuery = require("@kbn/es-query"); var _fieldTypes = require("@kbn/field-types"); var _autocomplete_operators = require("../autocomplete_operators"); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0 and the Server Side Public License, v 1; you may not use this file except * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ const isEntryNested = item => { return item.entries != null; }; exports.isEntryNested = isEntryNested; const filterExceptionItems = exceptions => { return exceptions.reduce((acc, exception) => { const entries = exception.entries.reduce((nestedAcc, singleEntry) => { const strippedSingleEntry = (0, _securitysolutionUtils.removeIdFromItem)(singleEntry); if (_securitysolutionIoTsListTypes.entriesNested.is(strippedSingleEntry)) { const nestedEntriesArray = strippedSingleEntry.entries.filter(singleNestedEntry => { const noIdSingleNestedEntry = (0, _securitysolutionUtils.removeIdFromItem)(singleNestedEntry); const [validatedNestedEntry] = (0, _securitysolutionIoTsUtils.validate)(noIdSingleNestedEntry, _securitysolutionIoTsListTypes.nestedEntryItem); return validatedNestedEntry != null; }); const noIdNestedEntries = nestedEntriesArray.map(singleNestedEntry => (0, _securitysolutionUtils.removeIdFromItem)(singleNestedEntry)); const [validatedNestedEntry] = (0, _securitysolutionIoTsUtils.validate)({ ...strippedSingleEntry, entries: noIdNestedEntries }, _securitysolutionIoTsListTypes.entriesNested); if (validatedNestedEntry != null) { return [...nestedAcc, { ...singleEntry, entries: nestedEntriesArray }]; } return nestedAcc; } else { const [validatedEntry] = (0, _securitysolutionIoTsUtils.validate)(strippedSingleEntry, _securitysolutionIoTsListTypes.entry); if (validatedEntry != null) { return [...nestedAcc, singleEntry]; } return nestedAcc; } }, []); if (entries.length === 0) { return acc; } const item = { ...exception, entries }; if (_securitysolutionIoTsListTypes.exceptionListItemSchema.is(item)) { return [...acc, item]; } else if (_securitysolutionIoTsListTypes.createExceptionListItemSchema.is(item) || _securitysolutionIoTsListTypes.createRuleExceptionListItemSchema.is(item)) { const { meta, ...rest } = item; const itemSansMetaId = { ...rest, meta: undefined }; return [...acc, itemSansMetaId]; } else { return acc; } }, []); }; exports.filterExceptionItems = filterExceptionItems; const addIdToEntries = entries => { return entries.map(singleEntry => { if (singleEntry.type === 'nested') { return (0, _securitysolutionUtils.addIdToItem)({ ...singleEntry, entries: singleEntry.entries.map(nestedEntry => (0, _securitysolutionUtils.addIdToItem)(nestedEntry)) }); } else { return (0, _securitysolutionUtils.addIdToItem)(singleEntry); } }); }; exports.addIdToEntries = addIdToEntries; const getNewExceptionItem = ({ listId, namespaceType, name }) => { return { comments: [], description: `Exception list item`, entries: addIdToEntries([{ field: '', operator: 'included', type: 'match', value: '' }]), item_id: undefined, list_id: listId, meta: { temporaryUuid: (0, _uuid.v4)() }, name, namespace_type: namespaceType, tags: [], type: 'simple' }; }; /** * Returns the operator type, may not need this if using io-ts types * * @param item a single ExceptionItem entry */ exports.getNewExceptionItem = getNewExceptionItem; const getOperatorType = item => { switch (item.type) { case 'match': return _securitysolutionIoTsListTypes.ListOperatorTypeEnum.MATCH; case 'match_any': return _securitysolutionIoTsListTypes.ListOperatorTypeEnum.MATCH_ANY; case 'wildcard': return _securitysolutionIoTsListTypes.ListOperatorTypeEnum.WILDCARD; case 'list': return _securitysolutionIoTsListTypes.ListOperatorTypeEnum.LIST; default: return _securitysolutionIoTsListTypes.ListOperatorTypeEnum.EXISTS; } }; /** * Determines operator selection (is/is not/is one of, etc.) * Default operator is "is" * * @param item a single ExceptionItem entry */ exports.getOperatorType = getOperatorType; const getExceptionOperatorSelect = item => { if (item.type === 'nested') { return _autocomplete_operators.isOperator; } else { const operatorType = getOperatorType(item); const foundOperator = _autocomplete_operators.ALL_OPERATORS.find(operatorOption => { return item.operator === operatorOption.operator && operatorType === operatorOption.type; }); return foundOperator != null ? foundOperator : _autocomplete_operators.isOperator; } }; /** * Returns the fields corresponding value for an entry * * @param item a single ExceptionItem entry */ exports.getExceptionOperatorSelect = getExceptionOperatorSelect; const getEntryValue = item => { switch (item.type) { case _securitysolutionIoTsListTypes.ListOperatorTypeEnum.MATCH: case _securitysolutionIoTsListTypes.ListOperatorTypeEnum.MATCH_ANY: case _securitysolutionIoTsListTypes.ListOperatorTypeEnum.WILDCARD: return item.value; case _securitysolutionIoTsListTypes.ListOperatorTypeEnum.EXISTS: return undefined; case _securitysolutionIoTsListTypes.ListOperatorTypeEnum.LIST: return item.list.id; default: return undefined; } }; /** * Determines whether an entire entry, exception item, or entry within a nested * entry needs to be removed * * @param exceptionItem * @param entryIndex index of given entry, for nested entries, this will correspond * to their parent index * @param nestedEntryIndex index of nested entry * */ exports.getEntryValue = getEntryValue; const getUpdatedEntriesOnDelete = (exceptionItem, entryIndex, nestedParentIndex) => { const itemOfInterest = exceptionItem.entries[nestedParentIndex != null ? nestedParentIndex : entryIndex]; if (nestedParentIndex != null && itemOfInterest.type === _securitysolutionIoTsListTypes.ListOperatorTypeEnum.NESTED) { const updatedEntryEntries = [...itemOfInterest.entries.slice(0, entryIndex), ...itemOfInterest.entries.slice(entryIndex + 1)]; if (updatedEntryEntries.length === 0) { return { ...exceptionItem, entries: [...exceptionItem.entries.slice(0, nestedParentIndex), ...exceptionItem.entries.slice(nestedParentIndex + 1)] }; } else { const { field } = itemOfInterest; const updatedItemOfInterest = { entries: updatedEntryEntries, field, id: itemOfInterest.id != null ? itemOfInterest.id : `${entryIndex}`, type: _securitysolutionIoTsListTypes.ListOperatorTypeEnum.NESTED }; return { ...exceptionItem, entries: [...exceptionItem.entries.slice(0, nestedParentIndex), updatedItemOfInterest, ...exceptionItem.entries.slice(nestedParentIndex + 1)] }; } } else { return { ...exceptionItem, entries: [...exceptionItem.entries.slice(0, entryIndex), ...exceptionItem.entries.slice(entryIndex + 1)] }; } }; /** * Returns filtered index patterns based on the field - if a user selects to * add nested entry, should only show nested fields, if item is the parent * field of a nested entry, we only display the parent field * * @param patterns DataViewBase containing available fields on rule index * @param item exception item entry * set to add a nested field */ exports.getUpdatedEntriesOnDelete = getUpdatedEntriesOnDelete; const getFilteredIndexPatterns = (patterns, item) => { if (item.nested === 'child' && item.parent != null) { // when user has selected a nested entry, only fields with the common parent are shown return { ...patterns, fields: patterns.fields.filter(indexField => { const subTypeNested = (0, _esQuery.getDataViewFieldSubtypeNested)(indexField); const fieldHasCommonParentPath = subTypeNested && item.parent != null && subTypeNested.nested.path === item.parent.parent.field; return fieldHasCommonParentPath; }).map(f => { const [fieldNameWithoutParentPath] = f.name.split('.').slice(-1); return { ...f, name: fieldNameWithoutParentPath }; }) }; } else if (item.nested === 'parent' && item.field != null) { // when user has selected a nested entry, right above it we show the common parent return { ...patterns, fields: [item.field] }; } else if (item.nested === 'parent' && item.field == null) { // when user selects to add a nested entry, only nested fields are shown as options return { ...patterns, fields: patterns.fields.filter(field => (0, _esQuery.isDataViewFieldSubtypeNested)(field)) }; } else { return patterns; } }; /** * Determines proper entry update when user selects new field * * @param item - current exception item entry values * @param newField - newly selected field * */ exports.getFilteredIndexPatterns = getFilteredIndexPatterns; const getEntryOnFieldChange = (item, newField) => { const { parent, entryIndex, nested } = item; const newChildFieldValue = newField != null ? newField.name.split('.').slice(-1)[0] : ''; if (nested === 'parent') { // For nested entries, when user first selects to add a nested // entry, they first see a row similar to what is shown for when // a user selects "exists", as soon as they make a selection // we can now identify the 'parent' and 'child' this is where // we first convert the entry into type "nested" const subTypeNested = (0, _esQuery.getDataViewFieldSubtypeNested)(newField); const newParentFieldValue = (subTypeNested === null || subTypeNested === void 0 ? void 0 : subTypeNested.nested.path) || ''; return { index: entryIndex, updatedEntry: { entries: [(0, _securitysolutionUtils.addIdToItem)({ field: newChildFieldValue != null ? newChildFieldValue : '', operator: _autocomplete_operators.isOperator.operator, type: _securitysolutionIoTsListTypes.ListOperatorTypeEnum.MATCH, value: '' })], field: newParentFieldValue, id: item.id, type: _securitysolutionIoTsListTypes.ListOperatorTypeEnum.NESTED } }; } else if (nested === 'child' && parent != null) { return { index: parent.parentIndex, updatedEntry: { ...parent.parent, entries: [...parent.parent.entries.slice(0, entryIndex), { field: newChildFieldValue != null ? newChildFieldValue : '', id: item.id, operator: _autocomplete_operators.isOperator.operator, type: _securitysolutionIoTsListTypes.ListOperatorTypeEnum.MATCH, value: '' }, ...parent.parent.entries.slice(entryIndex + 1)] } }; } else { return { index: entryIndex, updatedEntry: { field: newField != null ? newField.name : '', id: item.id, operator: _autocomplete_operators.isOperator.operator, type: _securitysolutionIoTsListTypes.ListOperatorTypeEnum.MATCH, value: '' } }; } }; /** * Determines proper entry update when user updates value * when operator is of type "list" * * @param item - current exception item entry values * @param newField - newly selected list * */ exports.getEntryOnFieldChange = getEntryOnFieldChange; const getEntryOnListChange = (item, newField) => { const { entryIndex, field, operator } = item; const { id, type } = newField; return { index: entryIndex, updatedEntry: { field: field != null ? field.name : '', id: item.id, list: { id, type }, operator: operator.operator, type: _securitysolutionIoTsListTypes.ListOperatorTypeEnum.LIST } }; }; /** * Determines proper entry update when user updates value * when operator is of type "match_any" * * @param item - current exception item entry values * @param newField - newly entered value * */ exports.getEntryOnListChange = getEntryOnListChange; const getEntryOnMatchAnyChange = (item, newField) => { const { nested, parent, entryIndex, field, operator } = item; if (nested != null && parent != null) { const fieldName = field != null ? field.name.split('.').slice(-1)[0] : ''; return { index: parent.parentIndex, updatedEntry: { ...parent.parent, entries: [...parent.parent.entries.slice(0, entryIndex), { field: fieldName, id: item.id, operator: operator.operator, type: _securitysolutionIoTsListTypes.ListOperatorTypeEnum.MATCH_ANY, value: newField }, ...parent.parent.entries.slice(entryIndex + 1)] } }; } else { return { index: entryIndex, updatedEntry: { field: field != null ? field.name : '', id: item.id, operator: operator.operator, type: _securitysolutionIoTsListTypes.ListOperatorTypeEnum.MATCH_ANY, value: newField } }; } }; /** * Determines proper entry update when user updates value * when operator is of type "match" * * @param item - current exception item entry values * @param newField - newly entered value * */ exports.getEntryOnMatchAnyChange = getEntryOnMatchAnyChange; const getEntryOnMatchChange = (item, newField) => { const { nested, parent, entryIndex, field, operator } = item; if (nested != null && parent != null) { const fieldName = field != null ? field.name.split('.').slice(-1)[0] : ''; return { index: parent.parentIndex, updatedEntry: { ...parent.parent, entries: [...parent.parent.entries.slice(0, entryIndex), { field: fieldName, id: item.id, operator: operator.operator, type: _securitysolutionIoTsListTypes.ListOperatorTypeEnum.MATCH, value: newField }, ...parent.parent.entries.slice(entryIndex + 1)] } }; } else { return { index: entryIndex, updatedEntry: { field: field != null ? field.name : '', id: item.id, operator: operator.operator, type: _securitysolutionIoTsListTypes.ListOperatorTypeEnum.MATCH, value: newField } }; } }; /** * Determines proper entry update when user updates value * when operator is of type "wildcard" * * @param item - current exception item entry values * @param newField - newly entered value * */ exports.getEntryOnMatchChange = getEntryOnMatchChange; const getEntryOnWildcardChange = (item, newField) => { const { nested, parent, entryIndex, field, operator } = item; if (nested != null && parent != null) { const fieldName = field != null ? field.name.split('.').slice(-1)[0] : ''; return { index: parent.parentIndex, updatedEntry: { ...parent.parent, entries: [...parent.parent.entries.slice(0, entryIndex), { field: fieldName, id: item.id, operator: operator.operator, type: _securitysolutionIoTsListTypes.ListOperatorTypeEnum.WILDCARD, value: newField }, ...parent.parent.entries.slice(entryIndex + 1)] } }; } else { return { index: entryIndex, updatedEntry: { field: field != null ? field.name : '', id: item.id, operator: operator.operator, type: _securitysolutionIoTsListTypes.ListOperatorTypeEnum.WILDCARD, value: newField } }; } }; /** * On operator change, determines whether value needs to be cleared or not * * @param field * @param selectedOperator * @param currentEntry * */ exports.getEntryOnWildcardChange = getEntryOnWildcardChange; const getEntryFromOperator = (selectedOperator, currentEntry) => { const isSameOperatorType = currentEntry.operator.type === selectedOperator.type; const fieldValue = currentEntry.field != null ? currentEntry.field.name : ''; switch (selectedOperator.type) { case 'match': return { field: fieldValue, id: currentEntry.id, operator: selectedOperator.operator, type: _securitysolutionIoTsListTypes.ListOperatorTypeEnum.MATCH, value: isSameOperatorType && typeof currentEntry.value === 'string' ? currentEntry.value : '' }; case 'match_any': return { field: fieldValue, id: currentEntry.id, operator: selectedOperator.operator, type: _securitysolutionIoTsListTypes.ListOperatorTypeEnum.MATCH_ANY, value: isSameOperatorType && Array.isArray(currentEntry.value) ? currentEntry.value : [] }; case 'list': return { field: fieldValue, id: currentEntry.id, list: { id: '', type: 'ip' }, operator: selectedOperator.operator, type: _securitysolutionIoTsListTypes.ListOperatorTypeEnum.LIST }; case 'wildcard': return { field: fieldValue, id: currentEntry.id, operator: selectedOperator.operator, type: _securitysolutionIoTsListTypes.ListOperatorTypeEnum.WILDCARD, value: isSameOperatorType && typeof currentEntry.value === 'string' ? currentEntry.value : '' }; default: return { field: fieldValue, id: currentEntry.id, operator: selectedOperator.operator, type: _securitysolutionIoTsListTypes.ListOperatorTypeEnum.EXISTS }; } }; /** * Determines proper entry update when user selects new operator * * @param item - current exception item entry values * @param newOperator - newly selected operator * */ exports.getEntryFromOperator = getEntryFromOperator; const getEntryOnOperatorChange = (item, newOperator) => { const { parent, entryIndex, field, nested } = item; const newEntry = getEntryFromOperator(newOperator, item); if (!_securitysolutionIoTsListTypes.entriesList.is(newEntry) && nested != null && parent != null) { return { index: parent.parentIndex, updatedEntry: { ...parent.parent, entries: [...parent.parent.entries.slice(0, entryIndex), { ...newEntry, field: field != null ? field.name.split('.').slice(-1)[0] : '' }, ...parent.parent.entries.slice(entryIndex + 1)] } }; } else { return { index: entryIndex, updatedEntry: newEntry }; } }; exports.getEntryOnOperatorChange = getEntryOnOperatorChange; const isKibanaStringType = type => { const kbnFieldType = (0, _fieldTypes.castEsToKbnFieldTypeName)(type); return kbnFieldType === _fieldTypes.KBN_FIELD_TYPES.STRING; }; exports.isKibanaStringType = isKibanaStringType; const fieldSupportsMatches = field => { var _field$esTypes; return (_field$esTypes = field.esTypes) === null || _field$esTypes === void 0 ? void 0 : _field$esTypes.some(isKibanaStringType); }; /** * Determines which operators to make available * * @param item * @param listType * @param isBoolean * @param includeValueListOperators whether or not to include the 'is in list' and 'is not in list' operators */ exports.fieldSupportsMatches = fieldSupportsMatches; const getOperatorOptions = (item, listType, isBoolean, includeValueListOperators = true) => { if (item.nested === 'parent' || item.field == null) { return [_autocomplete_operators.isOperator]; } else if (item.nested != null && listType === 'endpoint' || listType === 'endpoint') { return isBoolean ? [_autocomplete_operators.isOperator] : [_autocomplete_operators.isOperator, _autocomplete_operators.isOneOfOperator]; } else if (item.nested != null && listType === 'detection') { return isBoolean ? [_autocomplete_operators.isOperator, _autocomplete_operators.existsOperator] : [_autocomplete_operators.isOperator, _autocomplete_operators.isOneOfOperator, _autocomplete_operators.existsOperator]; } else if (isBoolean) { return [_autocomplete_operators.isOperator, _autocomplete_operators.isNotOperator, _autocomplete_operators.existsOperator, _autocomplete_operators.doesNotExistOperator]; } else if (!includeValueListOperators) { return fieldSupportsMatches(item.field) ? _autocomplete_operators.EXCEPTION_OPERATORS_SANS_LISTS : [_autocomplete_operators.isOperator, _autocomplete_operators.isNotOperator, _autocomplete_operators.isOneOfOperator, _autocomplete_operators.isNotOneOfOperator, _autocomplete_operators.existsOperator, _autocomplete_operators.doesNotExistOperator]; } else { return listType === 'detection' ? fieldSupportsMatches(item.field) ? _autocomplete_operators.DETECTION_ENGINE_EXCEPTION_OPERATORS : [_autocomplete_operators.isOperator, _autocomplete_operators.isNotOperator, _autocomplete_operators.isOneOfOperator, _autocomplete_operators.isNotOneOfOperator, _autocomplete_operators.existsOperator, _autocomplete_operators.doesNotExistOperator, _autocomplete_operators.isInListOperator, _autocomplete_operators.isNotInListOperator] : _autocomplete_operators.ALL_OPERATORS; } }; /** * Fields of type 'text' do not generate autocomplete values, we want * to find it's corresponding keyword type (if available) which does * generate autocomplete values * * @param fields DataViewFieldBase fields * @param selectedField the field name that was selected * @param isTextType we only want a corresponding keyword field if * the selected field is of type 'text' * */ exports.getOperatorOptions = getOperatorOptions; const getCorrespondingKeywordField = ({ fields, selectedField }) => { const selectedFieldBits = selectedField != null && selectedField !== '' ? selectedField.split('.') : []; const selectedFieldIsTextType = selectedFieldBits.slice(-1)[0] === 'text'; if (selectedFieldIsTextType && selectedFieldBits.length > 0) { const keywordField = selectedFieldBits.slice(0, selectedFieldBits.length - 1).join('.'); const [foundKeywordField] = fields.filter(({ name }) => keywordField !== '' && keywordField === name); return foundKeywordField; } return undefined; }; /** * Formats the entry into one that is easily usable for the UI, most of the * complexity was introduced with nested fields * * @param patterns DataViewBase containing available fields on rule index * @param item exception item entry * @param itemIndex entry index * @param parent nested entries hold copy of their parent for use in various logic * @param parentIndex corresponds to the entry index, this might seem obvious, but * was added to ensure that nested items could be identified with their parent entry * @param allowCustomFieldOptions determines if field must be found to match in indexPattern or not */ exports.getCorrespondingKeywordField = getCorrespondingKeywordField; const getFormattedBuilderEntry = (indexPattern, item, itemIndex, parent, parentIndex, allowCustomFieldOptions) => { const { fields } = indexPattern; const field = parent != null ? `${parent.field}.${item.field}` : item.field; const [foundField] = fields.filter(({ name }) => field != null && field === name); const correspondingKeywordField = getCorrespondingKeywordField({ fields, selectedField: field }); if (parent != null && parentIndex != null) { return { correspondingKeywordField, entryIndex: itemIndex, field: foundField != null ? { ...foundField, name: foundField.name.split('.').slice(-1)[0] } : foundField, id: item.id != null ? item.id : `${itemIndex}`, nested: 'child', operator: getExceptionOperatorSelect(item), parent: { parent, parentIndex }, value: getEntryValue(item) }; } else { const fieldToUse = allowCustomFieldOptions ? foundField !== null && foundField !== void 0 ? foundField : { name: item.field, type: 'keyword' } : foundField; return { correspondingKeywordField, entryIndex: itemIndex, field: fieldToUse, id: item.id != null ? item.id : `${itemIndex}`, nested: undefined, operator: getExceptionOperatorSelect(item), parent: undefined, value: getEntryValue(item) }; } }; /** * Formats the entries to be easily usable for the UI, most of the * complexity was introduced with nested fields * * @param patterns DataViewBase containing available fields on rule index * @param entries exception item entries * @param allowCustomFieldOptions determines if field must be found to match in indexPattern or not * @param parent nested entries hold copy of their parent for use in various logic * @param parentIndex corresponds to the entry index, this might seem obvious, but * was added to ensure that nested items could be identified with their parent entry */ exports.getFormattedBuilderEntry = getFormattedBuilderEntry; const getFormattedBuilderEntries = (indexPattern, entries, allowCustomFieldOptions, parent, parentIndex) => { return entries.reduce((acc, item, index) => { const isNewNestedEntry = item.type === 'nested' && item.entries.length === 0; if (item.type !== 'nested' && !isNewNestedEntry) { const newItemEntry = getFormattedBuilderEntry(indexPattern, item, index, parent, parentIndex, allowCustomFieldOptions); return [...acc, newItemEntry]; } else { const parentEntry = { correspondingKeywordField: undefined, entryIndex: index, field: isNewNestedEntry ? undefined : // This type below is really a FieldSpec type from "src/plugins/data/common/index_patterns/fields/types.ts", we cast it here to keep using the DataViewFieldBase interface { aggregatable: false, esTypes: ['nested'], name: item.field != null ? item.field : '', searchable: false, type: 'string' }, id: item.id != null ? item.id : `${index}`, nested: 'parent', operator: _autocomplete_operators.isOperator, parent: undefined, value: undefined }; // User has selected to add a nested field, but not yet selected the field if (isNewNestedEntry) { return [...acc, parentEntry]; } if (isEntryNested(item)) { const nestedItems = getFormattedBuilderEntries(indexPattern, item.entries, allowCustomFieldOptions, item, index); return [...acc, parentEntry, ...nestedItems]; } return [...acc]; } }, []); }; exports.getFormattedBuilderEntries = getFormattedBuilderEntries; const getDefaultEmptyEntry = () => ({ field: '', id: (0, _uuid.v4)(), operator: _securitysolutionIoTsListTypes.ListOperatorEnum.INCLUDED, type: _securitysolutionIoTsListTypes.ListOperatorTypeEnum.MATCH, value: '' }); exports.getDefaultEmptyEntry = getDefaultEmptyEntry; const getDefaultNestedEmptyEntry = () => ({ entries: [], field: '', id: (0, _uuid.v4)(), type: _securitysolutionIoTsListTypes.ListOperatorTypeEnum.NESTED }); exports.getDefaultNestedEmptyEntry = getDefaultNestedEmptyEntry; const containsValueListEntry = items => items.some(item => item.entries.some(({ type }) => type === _securitysolutionIoTsListTypes.ListOperatorTypeEnum.LIST)); exports.containsValueListEntry = containsValueListEntry; const buildShowActiveExceptionsFilter = savedObjectPrefix => { const now = new Date().toISOString(); const filters = savedObjectPrefix.map(prefix => `${prefix}.attributes.expire_time > "${now}" OR NOT ${prefix}.attributes.expire_time: *`); return filters.join(','); }; exports.buildShowActiveExceptionsFilter = buildShowActiveExceptionsFilter; const buildShowExpiredExceptionsFilter = savedObjectPrefix => { const now = new Date().toISOString(); const filters = savedObjectPrefix.map(prefix => `${prefix}.attributes.expire_time <= "${now}"`); return filters.join(','); }; exports.buildShowExpiredExceptionsFilter = buildShowExpiredExceptionsFilter; const getIndexGroupName = indexName => { // Check whether it is a Data Stream index const dataStreamExp = /.ds-(.*?)-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-[0-9]{6}/; let result = indexName.match(dataStreamExp); if (result && result.length === 2) { return result[1]; } // Check whether it is an old '.siem' index group const siemSignalsExp = /.siem-(.*?)-[0-9]{6}/; result = indexName.match(siemSignalsExp); if (result && result.length === 2) { return `.siem-${result[1]}`; } // Otherwise return index name return indexName; }; const getMappingConflictsInfo = field => { if (!field.conflictDescriptions) { return null; } const conflicts = []; for (const [key, value] of Object.entries(field.conflictDescriptions)) { const groupedIndices = []; // Group indices and calculate count of indices in each group const groupedInfo = {}; value.forEach(index => { const groupName = getIndexGroupName(index); if (!groupedInfo[groupName]) { groupedInfo[groupName] = 0; } groupedInfo[groupName]++; }); for (const [name, count] of Object.entries(groupedInfo)) { groupedIndices.push({ name, count }); } // Sort groups by the indices count groupedIndices.sort((group1, group2) => { return group2.count - group1.count; }); conflicts.push({ type: key, totalIndexCount: value.length, groupedIndices }); } return conflicts; }; exports.getMappingConflictsInfo = getMappingConflictsInfo;