[AppServices] Disallow creating runtime and scripted fields with * in the name (#110081) (#116119)

This commit is contained in:
Shivindera Singh 2021-10-26 16:45:45 +02:00 committed by GitHub
parent d59d0c92ca
commit 1581512816
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 91 additions and 16 deletions

View file

@ -10,7 +10,7 @@ import { map, last } from 'lodash';
import { IndexPattern } from './data_view';
import { DuplicateField } from '../../../kibana_utils/common';
import { CharacterNotAllowedInField, DuplicateField } from '../../../kibana_utils/common';
import { IndexPatternField } from '../fields';
@ -207,6 +207,14 @@ describe('IndexPattern', () => {
expect(e).toBeInstanceOf(DuplicateField);
}
});
test('should not allow scripted field with * in name', async () => {
try {
await indexPattern.addScriptedField('test*123', "'new script'", 'string');
} catch (e) {
expect(e).toBeInstanceOf(CharacterNotAllowedInField);
}
});
});
describe('setFieldFormat and deleteFieldFormaat', () => {
@ -267,6 +275,14 @@ describe('IndexPattern', () => {
});
expect(indexPattern.toSpec()!.fields!.new_field).toBeUndefined();
});
test('should not allow runtime field with * in name', async () => {
try {
await indexPattern.addRuntimeField('test*123', runtime);
} catch (e) {
expect(e).toBeInstanceOf(CharacterNotAllowedInField);
}
});
});
describe('getFormatterForField', () => {

View file

@ -13,7 +13,7 @@ import { castEsToKbnFieldTypeName, ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { FieldAttrs, FieldAttrSet, DataViewAttributes } from '..';
import type { RuntimeField } from '../types';
import { DuplicateField } from '../../../kibana_utils/common';
import { CharacterNotAllowedInField, DuplicateField } from '../../../kibana_utils/common';
import { IIndexPattern, IFieldType } from '../../common';
import { DataViewField, IIndexPatternFieldList, fieldList } from '../fields';
@ -237,6 +237,10 @@ export class DataView implements IIndexPattern {
const scriptedFields = this.getScriptedFields();
const names = _.map(scriptedFields, 'name');
if (name.includes('*')) {
throw new CharacterNotAllowedInField('*', name);
}
if (_.includes(names, name)) {
throw new DuplicateField(name);
}
@ -358,6 +362,11 @@ export class DataView implements IIndexPattern {
*/
addRuntimeField(name: string, runtimeField: RuntimeField) {
const existingField = this.getFieldByName(name);
if (name.includes('*')) {
throw new CharacterNotAllowedInField('*', name);
}
if (existingField) {
existingField.runtimeField = runtimeField;
} else {

View file

@ -11,7 +11,7 @@ import { fieldValidators } from '../../shared_imports';
import { RUNTIME_FIELD_OPTIONS } from './constants';
const { emptyField, numberGreaterThanField } = fieldValidators;
const { containsCharsField, emptyField, numberGreaterThanField } = fieldValidators;
export const schema = {
name: {
@ -29,6 +29,17 @@ export const schema = {
)
),
},
{
validator: containsCharsField({
message: i18n.translate(
'indexPatternFieldEditor.editor.form.validations.starCharacterNotAllowedValidationErrorMessage',
{
defaultMessage: 'The field cannot have * in the name.',
}
),
chars: '*',
}),
},
],
},
type: {

View file

@ -546,7 +546,7 @@ exports[`FieldEditor should show conflict field warning 1`] = `
onClose={[Function]}
/>
<eui-form-row
error={null}
error={false}
helpText={
<span>
<eui-icon
@ -1175,7 +1175,7 @@ exports[`FieldEditor should show multiple type field warning with a table contai
onClose={[Function]}
/>
<eui-form-row
error={null}
error={false}
helpText={
<span>
<eui-icon

View file

@ -292,4 +292,25 @@ describe('FieldEditor', () => {
component.update();
expect(component).toMatchSnapshot();
});
it('should not allow field to have * in the name', async () => {
const testField = {
...field,
name: 'test-field',
};
const component = createComponentWithContext<FieldEdiorProps>(
FieldEditor,
{
indexPattern,
spec: testField as unknown as IndexPatternField,
services,
},
mockContext
);
await new Promise((resolve) => process.nextTick(resolve));
(component.instance() as FieldEditor).onFieldChange('name', 'test*123');
component.update();
expect(component.html().includes('The field cannot have * in the name.')).toBe(true);
});
});

View file

@ -267,7 +267,8 @@ export class FieldEditor extends PureComponent<FieldEdiorProps, FieldEditorState
renderName() {
const { isCreating, spec } = this.state;
const isInvalid = !spec.name || !spec.name.trim();
const starCheck = spec?.name?.includes('*');
const isInvalid = !spec.name || !spec.name.trim() || starCheck;
return isCreating ? (
<EuiFormRow
@ -298,11 +299,17 @@ export class FieldEditor extends PureComponent<FieldEdiorProps, FieldEditorState
}
isInvalid={isInvalid}
error={
isInvalid
? i18n.translate('indexPatternManagement.nameErrorMessage', {
isInvalid &&
(starCheck
? i18n.translate(
'indexPatternManagement.starCharacterNotAllowedValidationErrorMessage',
{
defaultMessage: 'The field cannot have * in the name.',
}
)
: i18n.translate('indexPatternManagement.nameErrorMessage', {
defaultMessage: 'Name is required',
})
: null
}))
}
>
<EuiFieldText

View file

@ -26,6 +26,17 @@ export class DuplicateField extends KbnError {
}
}
/**
* when a user is attempting to create a field with disallowed character in the name, like *
* @param {String} character - the character not allowed in name
* @param {String} name - the field name
*/
export class CharacterNotAllowedInField extends KbnError {
constructor(character: string, name: string) {
super(`The field "${name}" cannot have "${character}" in the name`);
}
}
/**
* A saved object was not found
*/

View file

@ -132,7 +132,7 @@ describe('Runtime field editor', () => {
const defaultValue: RuntimeField = {
name: 'myRuntimeField',
type: 'boolean',
script: { source: 'emit("hello"' },
script: { source: 'emit("hello")' },
};
testBed = setup({

View file

@ -98,7 +98,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.lens.goToTimeRange();
await PageObjects.lens.switchToVisualization('lnsDatatable');
await PageObjects.lens.clickAddField();
await fieldEditor.setName(`*' "'`);
await fieldEditor.setName(`ab' "'`);
await fieldEditor.enableValue();
await fieldEditor.typeScript("emit('abc')");
await fieldEditor.save();
@ -106,21 +106,21 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.lens.configureDimension({
dimension: 'lnsDatatable_metrics > lns-empty-dimension',
operation: 'unique_count',
field: `*`,
field: `ab`,
keepOpen: true,
});
await PageObjects.lens.switchToFormula();
await PageObjects.lens.expectFormulaText(`unique_count('*\\' "\\'')`);
await PageObjects.lens.expectFormulaText(`unique_count('ab\\' "\\'')`);
await PageObjects.lens.typeFormula('unique_count(');
const input = await find.activeElement();
await input.type('*');
await input.type('ab');
await input.pressKeys(browser.keys.ENTER);
await PageObjects.common.sleep(100);
await PageObjects.lens.expectFormulaText(`unique_count('*\\' "\\'')`);
await PageObjects.lens.expectFormulaText(`unique_count('ab\\' "\\'')`);
});
it('should persist a broken formula on close', async () => {