in x-pack/platform/plugins/shared/fleet/server/routes/utils/filter_utils.ts [88:236]
isSavedObjectAttr: isSavedObjectAttr(key, indexMapping),
key,
type: getType(key),
},
];
}
return kueryNode;
}, []);
};
const getType = (key: string | undefined | null) => {
if (key != null && key.includes('.')) {
return key.split('.')[0];
} else if (allowedTerms.some((term) => term === key)) {
return 'searchTerm';
} else {
return null;
}
};
/**
* Is this filter key referring to a a top-level SavedObject attribute such as
* `updated_at` or `references`.
*
* @param key
* @param indexMapping
*/
export const isSavedObjectAttr = (key: string | null | undefined, indexMapping: IndexMapping) => {
const keySplit = key != null ? key.split('.') : [];
if (keySplit.length === 1 && fieldDefined(indexMapping, keySplit[0])) {
return true;
} else if (keySplit.length === 2 && keySplit[1] === 'id') {
return true;
} else if (keySplit.length === 2 && fieldDefined(indexMapping, keySplit[1])) {
return true;
} else {
return false;
}
};
export const hasFilterKeyError = (
key: string | null | undefined,
types: string[],
indexMapping: IndexMapping,
skipNormalization?: boolean
): string | null => {
if (key === null) {
return null;
}
if (!key) {
return `Invalid key`;
}
if (!key.includes('.')) {
if (allowedTerms.some((term) => term === key) || fieldDefined(indexMapping, key)) {
return null;
}
return `This type '${key}' is not allowed`;
} else if (key.includes('.')) {
const keySplit = key.split('.');
const firstField = keySplit[0];
const hasIndexWrap = types.includes(firstField);
if (keySplit.length <= 1 && !fieldDefined(indexMapping, firstField) && !hasIndexWrap) {
return `This type '${firstField}' is not allowed`;
}
// In some cases we don't want to check about the `attributes` presence
// In that case pass the `skipNormalization` parameter
if (
(!skipNormalization && keySplit.length === 2 && fieldDefined(indexMapping, key)) ||
(!skipNormalization && keySplit.length > 2 && keySplit[1] !== 'attributes')
) {
return `This key '${key}' does NOT match the filter proposition SavedObjectType.attributes.key`;
}
// Check that the key exists in the mappings
let searchKey = '';
if (keySplit.length === 2) {
searchKey = hasIndexWrap ? keySplit[1] : key;
} else if (keySplit.length > 2) {
searchKey =
skipNormalization || keySplit[1] !== 'attributes'
? `${firstField}.${keySplit.slice(1, keySplit.length).join('.')}`
: `${firstField}.${keySplit.slice(2, keySplit.length).join('.')}`;
}
if (!fieldDefined(indexMapping, searchKey)) {
return `This key '${key}' does NOT exist in ${types.join()} saved object index patterns`;
}
}
return null;
};
const getMappingKey = (key?: string) =>
!!key ? 'properties.' + key.split('.').join('.properties.') : '';
export const fieldDefined = (indexMappings: IndexMapping, key: string): boolean => {
const keySplit = key.split('.');
const shortenedKey = `${keySplit[1]}.${keySplit.slice(2, keySplit.length).join('.')}`;
const mappingKey = getMappingKey(key);
if (
!!get(indexMappings, mappingKey) ||
!!get(indexMappings, getMappingKey(shortenedKey)) ||
mappingKey === 'properties.id'
) {
return true;
}
// If the `mappingKey` does not match a valid path, before returning false,
// we want to check and see if the intended path was for a multi-field
// such as `x.attributes.field.text` where `field` is mapped to both text
// and keyword
const propertiesAttribute = 'properties';
const indexOfLastProperties = mappingKey.lastIndexOf(propertiesAttribute);
const fieldMapping = mappingKey.substr(0, indexOfLastProperties);
const fieldType = mappingKey.substr(
mappingKey.lastIndexOf(propertiesAttribute) + `${propertiesAttribute}.`.length
);
const mapping = `${fieldMapping}fields.${fieldType}`;
if (!!get(indexMappings, mapping)) {
return true;
}
// If the path is for a flattened type field, we'll assume the mappings are defined.
const keys = key.split('.');
for (let i = 0; i < keys.length; i++) {
const path = `properties.${keys.slice(0, i + 1).join('.properties.')}`;
if (get(indexMappings, path)?.type === 'flattened') {
return true;
}
}
return false;
};
export const validateKuery = (
kuery: string | undefined,
allowedTypes: string[],
indexMapping: IndexMapping,
skipNormalization?: boolean
) => {
let isValid = true;
let error: string | undefined;
if (!kuery) {
isValid = true;
}
try {
if (kuery && indexMapping) {
const astFilter = esKuery.fromKueryExpression(kuery);
const validationObject = validateFilterKueryNode({