Remove dangerously set inner html (#33976)

Fixes #24781
Replaces dangerouslySetInnerHtml with JSX using FormattedMessage for i18n.
This commit is contained in:
Christiane (Tina) Heiligers 2019-03-28 12:56:36 -07:00 committed by GitHub
parent 5f511fcd83
commit fd3535fdcf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 156 additions and 142 deletions

View file

@ -26,12 +26,9 @@ exports[`SuggestionComponent Should display the suggestion and use the provided
</div>
<div
className="kbnSuggestionItem__description"
dangerouslySetInnerHTML={
Object {
"__html": "This is not a helpful suggestion",
}
}
/>
>
This is not a helpful suggestion
</div>
</div>
</div>
`;
@ -62,12 +59,9 @@ exports[`SuggestionComponent Should make the element active if the selected prop
</div>
<div
className="kbnSuggestionItem__description"
dangerouslySetInnerHTML={
Object {
"__html": "This is not a helpful suggestion",
}
}
/>
>
This is not a helpful suggestion
</div>
</div>
</div>
`;

View file

@ -67,14 +67,7 @@ export const SuggestionComponent: SFC<Props> = props => {
<EuiIcon type={getEuiIconType(props.suggestion.type)} />
</div>
<div className="kbnSuggestionItem__text">{props.suggestion.text}</div>
<div
className="kbnSuggestionItem__description"
// Description currently always comes from us and we escape any potential user input
// at the time we're generating the description text
// eslint-disable-next-line react/no-danger
// @ts-ignore
dangerouslySetInnerHTML={{ __html: props.suggestion.description }}
/>
<div className="kbnSuggestionItem__description">{props.suggestion.description}</div>
</div>
</div>
);

View file

@ -113,9 +113,7 @@ function Suggestion(props) {
<EuiIcon type={getEuiIconType(props.suggestion.type)} />
</Icon>
<TextValue>{props.suggestion.text}</TextValue>
<Description
dangerouslySetInnerHTML={{ __html: props.suggestion.description }}
/>
<Description>{props.suggestion.description}</Description>
</ListItem>
);
}

View file

@ -36,9 +36,7 @@ export class SuggestionItem extends React.Component<SuggestionItemProps> {
<EuiIcon type={getEuiIconType(suggestion.type)} />
</SuggestionItemIconField>
<SuggestionItemTextField>{suggestion.text}</SuggestionItemTextField>
<SuggestionItemDescriptionField
dangerouslySetInnerHTML={{ __html: suggestion.description }}
/>
<SuggestionItemDescriptionField>{suggestion.description}</SuggestionItemDescriptionField>
</SuggestionItemContainer>
);
}

View file

@ -36,9 +36,7 @@ export class SuggestionItem extends React.Component<SuggestionItemProps> {
<EuiIcon type={getEuiIconType(suggestion.type)} />
</SuggestionItemIconField>
<SuggestionItemTextField>{suggestion.text}</SuggestionItemTextField>
<SuggestionItemDescriptionField
dangerouslySetInnerHTML={{ __html: suggestion.description }}
/>
<SuggestionItemDescriptionField>{suggestion.description}</SuggestionItemDescriptionField>
</SuggestionItemContainer>
);
}

View file

@ -35,13 +35,14 @@ describe('Kuery conjunction suggestions', function () {
expect(suggestions.map(suggestion => suggestion.start)).to.eql([end, end]);
expect(suggestions.map(suggestion => suggestion.end)).to.eql([end, end]);
});
it('should have descriptions', function () {
const text = ' ';
const suggestions = getSuggestions({ text });
expect(suggestions.length).to.be(2);
expect(typeof suggestions).to.be('object');
expect(Object.keys(suggestions).length).to.be(2);
suggestions.forEach(suggestion => {
expect(suggestion.description.length).to.be.greaterThan(0);
expect(typeof suggestion).to.be('object');
expect(suggestion).to.have.property('description');
});
});
});

View file

@ -77,7 +77,7 @@ describe('Kuery field suggestions', function () {
const suggestions = getSuggestions({ prefix, suffix });
expect(suggestions.length).to.be.greaterThan(0);
suggestions.forEach(suggestion => {
expect(suggestion.description.length).to.be.greaterThan(0);
expect(suggestion).to.have.property('description');
});
});
});

View file

@ -53,7 +53,7 @@ describe('Kuery operator suggestions', function () {
const suggestions = getSuggestions({ fieldName });
expect(suggestions.length).to.be.greaterThan(0);
suggestions.forEach(suggestion => {
expect(suggestion.description.length).to.be.greaterThan(0);
expect(suggestion).to.have.property('description');
});
});
});

View file

@ -3,37 +3,48 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
const type = 'conjunction';
const bothArgumentsText = i18n.translate('xpack.kueryAutocomplete.andOperatorDescription.bothArgumentsText', {
defaultMessage: 'both arguments',
description: 'Part of xpack.kueryAutocomplete.andOperatorDescription. Full text: "Requires both arguments to be true"'
});
const oneOrMoreArgumentsText = i18n.translate('xpack.kueryAutocomplete.orOperatorDescription.oneOrMoreArgumentsText', {
defaultMessage: 'one or more arguments',
description: 'Part of xpack.kueryAutocomplete.orOperatorDescription. Full text: "Requires one or more arguments to be true"'
});
const bothArgumentsText =
(<FormattedMessage
id="xpack.kueryAutocomplete.andOperatorDescription.bothArgumentsText"
defaultMessage="both arguments"
description="Part of xpack.kueryAutocomplete.andOperatorDescription. Full text: 'Requires both arguments to be true'"
/>);
const oneOrMoreArgumentsText =
(<FormattedMessage
id="xpack.kueryAutocomplete.orOperatorDescription.oneOrMoreArgumentsText"
defaultMessage="one or more arguments"
description="Part of xpack.kueryAutocomplete.orOperatorDescription. Full text: 'Requires one or more arguments to be true'"
/>);
const conjunctions = {
and: '<p>' +
i18n.translate('xpack.kueryAutocomplete.andOperatorDescription', {
defaultMessage: 'Requires {bothArguments} to be true',
values: { bothArguments: `<span class="kbnSuggestionItem__callout">${bothArgumentsText}</span>` },
description: 'Full text: "Requires both arguments to be true". See ' +
'xpack.kueryAutocomplete.andOperatorDescription.bothArgumentsText for "both arguments" part.'
}) +
'</p>',
or: '<p>' +
i18n.translate('xpack.kueryAutocomplete.orOperatorDescription', {
defaultMessage: 'Requires {oneOrMoreArguments} to be true',
values: { oneOrMoreArguments: `<span class="kbnSuggestionItem__callout">${oneOrMoreArgumentsText}</span>` },
description: 'Full text: "Requires one or more arguments to be true". See ' +
'xpack.kueryAutocomplete.orOperatorDescription.oneOrMoreArgumentsText for "one or more arguments" part.'
}) +
'</p>'
and: (
<p>
<FormattedMessage
id="xpack.kueryAutocomplete.andOperatorDescription"
defaultMessage="Requires {bothArguments} to be true"
values={{ bothArguments: <span className="kbnSuggestionItem__callout">{bothArgumentsText}</span> }}
description="Full text: ' Requires both arguments to be true'. See
'xpack.kueryAutocomplete.andOperatorDescription.bothArgumentsText' for 'both arguments' part."
/>
</p>
),
or: (
<p>
<FormattedMessage
id="xpack.kueryAutocomplete.orOperatorDescription"
defaultMessage="Requires {oneOrMoreArguments} to be true"
values={{ oneOrMoreArguments: <span className="kbnSuggestionItem__callout">{oneOrMoreArgumentsText}</span> }}
description="Full text: 'Requires one or more arguments to be true'. See
'xpack.kueryAutocomplete.orOperatorDescription.oneOrMoreArgumentsText' for 'one or more arguments' part."
/>
</p>
)
};
function getDescription(conjunction) {

View file

@ -3,23 +3,26 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { escape, flatten } from 'lodash';
import React from 'react';
import { flatten } from 'lodash';
import { escapeKuery } from './escape_kuery';
import { sortPrefixFirst } from 'ui/utils/sort_prefix_first';
import { isFilterable } from 'ui/index_patterns/static_utils';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
const type = 'field';
function getDescription(fieldName) {
return '<p>' +
i18n.translate('xpack.kueryAutocomplete.filterResultsDescription', {
defaultMessage: 'Filter results that contain {fieldName}',
values: { fieldName: `<span class="kbnSuggestionItem__callout">${escape(fieldName)}</span>` }
}) +
'</p>';
return (
<p>
<FormattedMessage
id="xpack.kueryAutocomplete.filterResultsDescription"
defaultMessage="Filter results that contain {fieldName}"
values={{ fieldName: <span className="kbnSuggestionItem__callout">{fieldName}</span> }}
/>
</p>
);
}
export function getSuggestionsProvider({ indexPatterns }) {

View file

@ -4,95 +4,119 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { flatten } from 'lodash';
const type = 'operator';
const equalsText = i18n.translate('xpack.kueryAutocomplete.equalOperatorDescription.equalsText', {
defaultMessage: 'equals',
description: 'Part of xpack.kueryAutocomplete.equalOperatorDescription. Full text: "equals some value"'
});
const lessThanOrEqualToText = i18n.translate('xpack.kueryAutocomplete.lessThanOrEqualOperatorDescription.lessThanOrEqualToText', {
defaultMessage: 'less than or equal to',
description: 'Part of xpack.kueryAutocomplete.lessThanOrEqualOperatorDescription. Full text: "is less than or equal to some value"'
});
const greaterThanOrEqualToText = i18n.translate('xpack.kueryAutocomplete.greaterThanOrEqualOperatorDescription.greaterThanOrEqualToText', {
defaultMessage: 'greater than or equal to',
description: 'Part of xpack.kueryAutocomplete.greaterThanOrEqualOperatorDescription. Full text: "is greater than or equal to some value"'
});
const lessThanText = i18n.translate('xpack.kueryAutocomplete.lessThanOperatorDescription.lessThanText', {
defaultMessage: 'less than',
description: 'Part of xpack.kueryAutocomplete.lessThanOperatorDescription. Full text: "is less than some value"'
});
const greaterThanText = i18n.translate('xpack.kueryAutocomplete.greaterThanOperatorDescription.greaterThanText', {
defaultMessage: 'greater than',
description: 'Part of xpack.kueryAutocomplete.greaterThanOperatorDescription. Full text: "is greater than some value"'
});
const existsText = i18n.translate('xpack.kueryAutocomplete.existOperatorDescription.existsText', {
defaultMessage: 'exists',
description: 'Part of xpack.kueryAutocomplete.existOperatorDescription. Full text: "exists in any form"'
});
const equalsText = (
<FormattedMessage
id="xpack.kueryAutocomplete.equalOperatorDescription.equalsText"
defaultMessage="equals"
description="Part of xpack.kueryAutocomplete.equalOperatorDescription. Full text: 'equals some value'"
/>);
const lessThanOrEqualToText = (
<FormattedMessage
id="xpack.kueryAutocomplete.lessThanOrEqualOperatorDescription.lessThanOrEqualToText"
defaultMessage="less than or equal to"
description="Part of xpack.kueryAutocomplete.lessThanOrEqualOperatorDescription. Full text: 'is less than or equal to some value'"
/>);
const greaterThanOrEqualToText = (
<FormattedMessage
id="xpack.kueryAutocomplete.greaterThanOrEqualOperatorDescription.greaterThanOrEqualToText"
defaultMessage="greater than or equal to"
description="Part of xpack.kueryAutocomplete.greaterThanOrEqualOperatorDescription. Full text: 'is greater than or equal to some value'"
/>);
const lessThanText = (
<FormattedMessage
id="xpack.kueryAutocomplete.lessThanOperatorDescription.lessThanText"
defaultMessage="less than"
description="Part of xpack.kueryAutocomplete.lessThanOperatorDescription. Full text: 'is less than some value'"
/>);
const greaterThanText = (
<FormattedMessage
id="xpack.kueryAutocomplete.greaterThanOperatorDescription.greaterThanText"
defaultMessage="greater than"
description="Part of xpack.kueryAutocomplete.greaterThanOperatorDescription. Full text: 'is greater than some value'"
/>);
const existsText = (
<FormattedMessage
id="xpack.kueryAutocomplete.existOperatorDescription.existsText"
defaultMessage="exists"
description="Part of xpack.kueryAutocomplete.existOperatorDescription. Full text: 'exists in any form'"
/>);
const operators = {
':': {
description: i18n.translate('xpack.kueryAutocomplete.equalOperatorDescription', {
defaultMessage: '{equals} some value',
values: { equals: `<span class="kbnSuggestionItem__callout">${equalsText}</span>` },
description: 'Full text: "equals some value". See ' +
'xpack.kueryAutocomplete.equalOperatorDescription.equalsText for "equals" part.'
}),
description: (
<FormattedMessage
id="xpack.kueryAutocomplete.equalOperatorDescription"
defaultMessage="{equals} some value"
values={{ equals: <span className="kbnSuggestionItem__callout">{equalsText}</span> }}
description="Full text: 'equals some value'. See
'xpack.kueryAutocomplete.equalOperatorDescription.equalsText' for 'equals' part."
/>),
fieldTypes: ['string', 'number', 'date', 'ip', 'geo_point', 'geo_shape', 'boolean']
},
'<=': {
description: i18n.translate('xpack.kueryAutocomplete.lessThanOrEqualOperatorDescription', {
defaultMessage: 'is {lessThanOrEqualTo} some value',
values: { lessThanOrEqualTo: `<span class="kbnSuggestionItem__callout">${lessThanOrEqualToText}</span>` },
description: 'Full text: "is less than or equal to some value". See ' +
'xpack.kueryAutocomplete.lessThanOrEqualOperatorDescription.lessThanOrEqualToText for "less than or equal to" part.'
}),
description: (
<FormattedMessage
id="xpack.kueryAutocomplete.lessThanOrEqualOperatorDescription"
defaultMessage="is {lessThanOrEqualTo} some value"
values={{ lessThanOrEqualTo: <span className="kbnSuggestionItem__callout">{lessThanOrEqualToText}</span> }}
description="Full text: 'is less than or equal to some value'. See
'xpack.kueryAutocomplete.lessThanOrEqualOperatorDescription.lessThanOrEqualToText' for 'less than or equal to' part."
/>),
fieldTypes: ['number', 'date', 'ip']
},
'>=': {
description: i18n.translate('xpack.kueryAutocomplete.greaterThanOrEqualOperatorDescription', {
defaultMessage: 'is {greaterThanOrEqualTo} some value',
values: { greaterThanOrEqualTo: `<span class="kbnSuggestionItem__callout">${greaterThanOrEqualToText}</span>` },
description: 'Full text: "is greater than or equal to some value". See ' +
'xpack.kueryAutocomplete.greaterThanOrEqualOperatorDescription.greaterThanOrEqualToText for "greater than or equal to" part.'
}),
description: (
<FormattedMessage
id="xpack.kueryAutocomplete.greaterThanOrEqualOperatorDescription"
defaultMessage="is {greaterThanOrEqualTo} some value"
values={{ greaterThanOrEqualTo: <span className="kbnSuggestionItem__callout">{greaterThanOrEqualToText}</span> }}
description="Full text: 'is greater than or equal to some value'. See
'xpack.kueryAutocomplete.greaterThanOrEqualOperatorDescription.greaterThanOrEqualToText' for 'greater than or equal to' part."
/>),
fieldTypes: ['number', 'date', 'ip']
},
'<': {
description: i18n.translate('xpack.kueryAutocomplete.lessThanOperatorDescription', {
defaultMessage: 'is {lessThan} some value',
values: { lessThan: `<span class="kbnSuggestionItem__callout">${lessThanText}</span>` },
description: 'Full text: "is less than some value". See ' +
'xpack.kueryAutocomplete.lessThanOperatorDescription.lessThanText for "less than" part.'
}),
description: (
<FormattedMessage
id="xpack.kueryAutocomplete.lessThanOperatorDescription"
defaultMessage="is {lessThan} some value"
values={{ lessThan: <span className="kbnSuggestionItem__callout">{lessThanText}</span> }}
description="Full text: 'is less than some value'. See
'xpack.kueryAutocomplete.lessThanOperatorDescription.lessThanText' for 'less than' part."
/>),
fieldTypes: ['number', 'date', 'ip']
},
'>': {
description: i18n.translate('xpack.kueryAutocomplete.greaterThanOperatorDescription', {
defaultMessage: 'is {greaterThan} some value',
values: { greaterThan: `<span class="kbnSuggestionItem__callout">${greaterThanText}</span>` },
description: 'Full text: "is greater than some value". See ' +
'xpack.kueryAutocomplete.greaterThanOperatorDescription.greaterThanText for "greater than" part.'
}),
description: (
<FormattedMessage
id="xpack.kueryAutocomplete.greaterThanOperatorDescription"
defaultMessage="is {greaterThan} some value"
values={{ greaterThan: <span className="kbnSuggestionItem__callout">{greaterThanText}</span> }}
description="Full text: 'is greater than some value'. See
'xpack.kueryAutocomplete.greaterThanOperatorDescription.greaterThanText' for 'greater than' part."
/>),
fieldTypes: ['number', 'date', 'ip']
},
':*': {
description: i18n.translate('xpack.kueryAutocomplete.existOperatorDescription', {
defaultMessage: '{exists} in any form',
values: { exists: `<span class="kbnSuggestionItem__callout">${existsText}</span>` },
description: 'Full text: "exists in any form". See ' +
'xpack.kueryAutocomplete.existOperatorDescription.existsText for "exists" part.'
})
description: (
<FormattedMessage
id="xpack.kueryAutocomplete.existOperatorDescription"
defaultMessage="{exists} in any form"
values={{ exists: <span className="kbnSuggestionItem__callout">{existsText}</span> }}
description="Full text: 'exists in any form'. See
'xpack.kueryAutocomplete.existOperatorDescription.existsText' for 'exists' part."
/>)
},
};
function getDescription(operator) {
const { description } = operators[operator];
return `<p>${description}</p>`;
return <p>{description}</p>;
}
export function getSuggestionsProvider({ indexPatterns }) {

View file

@ -17,12 +17,8 @@ exports[`Suggestion snapshot 1`] = `
<styled.div>
fieldValue
</styled.div>
<styled.div
dangerouslySetInnerHTML={
Object {
"__html": "<p>Test description for fieldValue</p>",
}
}
/>
<styled.div>
&lt;p&gt;Test description for fieldValue&lt;/p&gt;
</styled.div>
</styled.li>
`;

View file

@ -106,9 +106,7 @@ export const Suggestion = (props) => {
<EuiIcon type={getEuiIconType(props.suggestion.type)} />
</Icon>
<TextValue>{props.suggestion.text}</TextValue>
<Description
dangerouslySetInnerHTML={{ __html: props.suggestion.description }}
/>
<Description>{props.suggestion.description}</Description>
</ListItem>
);
};