mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-25 05:34:05 -08:00
React UI: Sanitize HTML string to allow only supported elements (#6165)
* Add component to sanitize html Signed-off-by: Ritesh Shrivastav <ritesh.conf@gmail.com> * Use SanitizeHTML component to allow only supported elements Signed-off-by: Ritesh Shrivastav <ritesh.conf@gmail.com> * Add allowedTags props in SanitizeHTML component Signed-off-by: Ritesh Shrivastav <ritesh.conf@gmail.com>
This commit is contained in:
parent
90c3615a2c
commit
0f91ff4540
|
@ -11,6 +11,7 @@
|
|||
"@types/node": "^12.11.1",
|
||||
"@types/react": "^16.8.2",
|
||||
"@types/react-dom": "^16.8.0",
|
||||
"@types/sanitize-html": "^1.20.2",
|
||||
"@types/react-resize-detector": "^4.0.2",
|
||||
"bootstrap": "^4.2.1",
|
||||
"downshift": "^3.2.2",
|
||||
|
@ -25,6 +26,7 @@
|
|||
"popper.js": "^1.14.3",
|
||||
"react": "^16.7.0",
|
||||
"react-dom": "^16.7.0",
|
||||
"sanitize-html": "^1.20.1",
|
||||
"react-resize-detector": "^4.2.1",
|
||||
"react-scripts": "^3.2.0",
|
||||
"reactstrap": "^8.0.1",
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
|
||||
import Downshift from 'downshift';
|
||||
import fuzzy from 'fuzzy';
|
||||
import SanitizeHTML from './components/SanitizeHTML';
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
@ -72,9 +73,9 @@ class ExpressionInput extends Component<ExpressionInputProps> {
|
|||
},
|
||||
})}
|
||||
>
|
||||
{/* TODO: Find better way than setting inner HTML dangerously. We just want the <strong> to not be escaped.
|
||||
This will be a problem when we save history and the user enters HTML into a query. */}
|
||||
<span dangerouslySetInnerHTML={{__html: item.string}}></span>
|
||||
<SanitizeHTML inline={true} allowedTags={['strong']}>
|
||||
{item.string}
|
||||
</SanitizeHTML>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
|
@ -95,63 +96,68 @@ class ExpressionInput extends Component<ExpressionInputProps> {
|
|||
|
||||
render() {
|
||||
return (
|
||||
<Downshift
|
||||
//inputValue={this.props.value}
|
||||
//onInputValueChange={this.props.onChange}
|
||||
selectedItem={this.props.value}
|
||||
>
|
||||
{(downshift) => (
|
||||
<div>
|
||||
<InputGroup className="expression-input">
|
||||
<InputGroupAddon addonType="prepend">
|
||||
<InputGroupText>
|
||||
{this.props.loading ? <FontAwesomeIcon icon="spinner" spin/> : <FontAwesomeIcon icon="search"/>}
|
||||
</InputGroupText>
|
||||
</InputGroupAddon>
|
||||
|
||||
<Input
|
||||
autoFocus
|
||||
type="textarea"
|
||||
rows="1"
|
||||
onKeyPress={this.handleKeyPress}
|
||||
placeholder="Expression (press Shift+Enter for newlines)"
|
||||
innerRef={this.exprInputRef}
|
||||
{...downshift.getInputProps({
|
||||
onKeyDown: (event: React.KeyboardEvent): void => {
|
||||
switch (event.key) {
|
||||
case 'Home':
|
||||
case 'End':
|
||||
// We want to be able to jump to the beginning/end of the input field.
|
||||
// By default, Downshift otherwise jumps to the first/last suggestion item instead.
|
||||
<Downshift
|
||||
//inputValue={this.props.value}
|
||||
//onInputValueChange={this.props.onChange}
|
||||
selectedItem={this.props.value}
|
||||
>
|
||||
{(downshift) => (
|
||||
<div>
|
||||
<InputGroup className="expression-input">
|
||||
<InputGroupAddon addonType="prepend">
|
||||
<InputGroupText>
|
||||
{this.props.loading ? <FontAwesomeIcon icon="spinner" spin/> : <FontAwesomeIcon icon="search"/>}
|
||||
</InputGroupText>
|
||||
</InputGroupAddon>
|
||||
<Input
|
||||
autoFocus
|
||||
type="textarea"
|
||||
rows="1"
|
||||
onKeyPress={this.handleKeyPress}
|
||||
placeholder="Expression (press Shift+Enter for newlines)"
|
||||
innerRef={this.exprInputRef}
|
||||
{...downshift.getInputProps({
|
||||
onKeyDown: (event: React.KeyboardEvent): void => {
|
||||
switch (event.key) {
|
||||
case 'Home':
|
||||
case 'End':
|
||||
// We want to be able to jump to the beginning/end of the input field.
|
||||
// By default, Downshift otherwise jumps to the first/last suggestion item instead.
|
||||
(event.nativeEvent as any).preventDownshiftDefault = true;
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
case 'ArrowDown':
|
||||
if (!downshift.isOpen) {
|
||||
(event.nativeEvent as any).preventDownshiftDefault = true;
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
case 'ArrowDown':
|
||||
if (!downshift.isOpen) {
|
||||
(event.nativeEvent as any).preventDownshiftDefault = true;
|
||||
}
|
||||
break;
|
||||
case 'Enter':
|
||||
downshift.closeMenu();
|
||||
break;
|
||||
case 'Escape':
|
||||
if (!downshift.isOpen) {
|
||||
this.exprInputRef.current!.blur();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'Enter':
|
||||
downshift.closeMenu();
|
||||
break;
|
||||
case 'Escape':
|
||||
if (!downshift.isOpen) {
|
||||
this.exprInputRef.current!.blur();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
} as any)}
|
||||
/>
|
||||
<InputGroupAddon addonType="append">
|
||||
<Button className="execute-btn" color="primary" onClick={() => this.props.executeQuery(this.exprInputRef.current!.value)}>Execute</Button>
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
{this.renderAutosuggest(downshift)}
|
||||
</div>
|
||||
)}
|
||||
</Downshift>
|
||||
}
|
||||
} as any)}
|
||||
/>
|
||||
<InputGroupAddon addonType="append">
|
||||
<Button
|
||||
className="execute-btn"
|
||||
color="primary"
|
||||
onClick={() => this.props.executeQuery(this.exprInputRef.current!.value)}
|
||||
>
|
||||
Execute
|
||||
</Button>
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
{this.renderAutosuggest(downshift)}
|
||||
</div>
|
||||
)}
|
||||
</Downshift>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
30
web/ui/react-app/src/components/SanitizeHTML/index.tsx
Normal file
30
web/ui/react-app/src/components/SanitizeHTML/index.tsx
Normal file
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* SanitizeHTML to render HTML, this takes care of sanitizing HTML.
|
||||
*/
|
||||
import React, { PureComponent } from 'react';
|
||||
import sanitizeHTML from 'sanitize-html';
|
||||
|
||||
interface SanitizeHTMLProps {
|
||||
inline: Boolean;
|
||||
allowedTags: string[];
|
||||
children: Element | string;
|
||||
}
|
||||
|
||||
class SanitizeHTML extends PureComponent<SanitizeHTMLProps> {
|
||||
sanitize = (html: any) => {
|
||||
return sanitizeHTML(html, {
|
||||
allowedTags: this.props.allowedTags
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { inline, children } = this.props;
|
||||
return inline ? (
|
||||
<span dangerouslySetInnerHTML={{ __html: this.sanitize(children) }} />
|
||||
) : (
|
||||
<div dangerouslySetInnerHTML={{ __html: this.sanitize(children) }} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SanitizeHTML;
|
12
web/ui/react-app/src/components/SanitizeHTML/test.js
vendored
Normal file
12
web/ui/react-app/src/components/SanitizeHTML/test.js
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
/**
|
||||
* SanitizeHTML tests
|
||||
*/
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import SanitizeHTML from '../SanitizeHTML';
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<SanitizeHTML />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
|
@ -1275,6 +1275,18 @@
|
|||
dependencies:
|
||||
"@babel/types" "^7.3.0"
|
||||
|
||||
"@types/domhandler@*":
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/domhandler/-/domhandler-2.4.1.tgz#7b3b347f7762180fbcb1ece1ce3dd0ebbb8c64cf"
|
||||
integrity sha512-cfBw6q6tT5sa1gSPFSRKzF/xxYrrmeiut7E0TxNBObiLSBTuFEHibcfEe3waQPEDbqBsq+ql/TOniw65EyDFMA==
|
||||
|
||||
"@types/domutils@*":
|
||||
version "1.7.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/domutils/-/domutils-1.7.2.tgz#89422e579c165994ad5c09ce90325da596cc105d"
|
||||
integrity sha512-Nnwy1Ztwq42SSNSZSh9EXBJGrOZPR+PQ2sRT4VZy8hnsFXfCil7YlKO2hd2360HyrtFz2qwnKQ13ENrgXNxJbw==
|
||||
dependencies:
|
||||
"@types/domhandler" "*"
|
||||
|
||||
"@types/eslint-visitor-keys@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
|
||||
|
@ -1287,6 +1299,15 @@
|
|||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
|
||||
"@types/htmlparser2@*":
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/htmlparser2/-/htmlparser2-3.10.1.tgz#1e65ba81401d53f425c1e2ba5a3d05c90ab742c7"
|
||||
integrity sha512-fCxmHS4ryCUCfV9+CJZY1UjkbR+6Al/EQdX5Jh03qBj9gdlPG5q+7uNoDgE/ZNXb3XNWSAQgqKIWnbRCbOyyWA==
|
||||
dependencies:
|
||||
"@types/domhandler" "*"
|
||||
"@types/domutils" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff"
|
||||
|
@ -1338,7 +1359,7 @@
|
|||
dependencies:
|
||||
moment ">=2.14.0"
|
||||
|
||||
"@types/node@^12.11.1":
|
||||
"@types/node@*", "@types/node@^12.11.1":
|
||||
version "12.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.11.1.tgz#1fd7b821f798b7fa29f667a1be8f3442bb8922a3"
|
||||
integrity sha512-TJtwsqZ39pqcljJpajeoofYRfeZ7/I/OMUQ5pR4q5wOKf2ocrUvBAZUMhWsOvKx3dVc/aaV5GluBivt0sWqA5A==
|
||||
|
@ -1383,6 +1404,13 @@
|
|||
"@types/react" "*"
|
||||
popper.js "^1.14.1"
|
||||
|
||||
"@types/sanitize-html@^1.20.2":
|
||||
version "1.20.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/sanitize-html/-/sanitize-html-1.20.2.tgz#59777f79f015321334e3a9f28882f58c0a0d42b8"
|
||||
integrity sha512-SrefiiBebGIhxEFkpbbYOwO1S6+zQLWAC4s4tipchlHq1aO9bp0xiapM7Zm0ml20MF+3OePWYdksB1xtneKPxg==
|
||||
dependencies:
|
||||
"@types/htmlparser2" "*"
|
||||
|
||||
"@types/sizzle@*":
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47"
|
||||
|
@ -1837,7 +1865,7 @@ array-union@^1.0.1:
|
|||
dependencies:
|
||||
array-uniq "^1.0.1"
|
||||
|
||||
array-uniq@^1.0.1:
|
||||
array-uniq@^1.0.1, array-uniq@^1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
|
||||
integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=
|
||||
|
@ -4815,7 +4843,7 @@ html-webpack-plugin@4.0.0-beta.5:
|
|||
tapable "^1.1.0"
|
||||
util.promisify "1.0.0"
|
||||
|
||||
htmlparser2@^3.3.0:
|
||||
htmlparser2@^3.10.0, htmlparser2@^3.3.0:
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f"
|
||||
integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==
|
||||
|
@ -6224,6 +6252,16 @@ lodash._reinterpolate@^3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
|
||||
integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=
|
||||
|
||||
lodash.clonedeep@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
|
||||
integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
|
||||
|
||||
lodash.escaperegexp@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347"
|
||||
integrity sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=
|
||||
|
||||
lodash.isfunction@^3.0.9:
|
||||
version "3.0.9"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051"
|
||||
|
@ -6234,11 +6272,26 @@ lodash.isobject@^3.0.2:
|
|||
resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d"
|
||||
integrity sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0=
|
||||
|
||||
lodash.isplainobject@^4.0.6:
|
||||
version "4.0.6"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
|
||||
integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=
|
||||
|
||||
lodash.isstring@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
|
||||
integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=
|
||||
|
||||
lodash.memoize@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
||||
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
|
||||
|
||||
lodash.mergewith@^4.6.1:
|
||||
version "4.6.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55"
|
||||
integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==
|
||||
|
||||
lodash.sortby@^4.7.0:
|
||||
version "4.7.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
||||
|
@ -8951,6 +9004,22 @@ sane@^4.0.3:
|
|||
minimist "^1.1.1"
|
||||
walker "~1.0.5"
|
||||
|
||||
sanitize-html@^1.20.1:
|
||||
version "1.20.1"
|
||||
resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-1.20.1.tgz#f6effdf55dd398807171215a62bfc21811bacf85"
|
||||
integrity sha512-txnH8TQjaQvg2Q0HY06G6CDJLVYCpbnxrdO0WN8gjCKaU5J0KbyGYhZxx5QJg3WLZ1lB7XU9kDkfrCXUozqptA==
|
||||
dependencies:
|
||||
chalk "^2.4.1"
|
||||
htmlparser2 "^3.10.0"
|
||||
lodash.clonedeep "^4.5.0"
|
||||
lodash.escaperegexp "^4.1.2"
|
||||
lodash.isplainobject "^4.0.6"
|
||||
lodash.isstring "^4.0.1"
|
||||
lodash.mergewith "^4.6.1"
|
||||
postcss "^7.0.5"
|
||||
srcset "^1.0.0"
|
||||
xtend "^4.0.1"
|
||||
|
||||
sass-loader@7.2.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-7.2.0.tgz#e34115239309d15b2527cb62b5dfefb62a96ff7f"
|
||||
|
@ -9355,6 +9424,14 @@ sprintf-js@~1.0.2:
|
|||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
|
||||
|
||||
srcset@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/srcset/-/srcset-1.0.0.tgz#a5669de12b42f3b1d5e83ed03c71046fc48f41ef"
|
||||
integrity sha1-pWad4StC87HV6D7QPHEEb8SPQe8=
|
||||
dependencies:
|
||||
array-uniq "^1.0.2"
|
||||
number-is-nan "^1.0.0"
|
||||
|
||||
sshpk@^1.7.0:
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
|
||||
|
@ -10605,7 +10682,7 @@ xregexp@4.0.0:
|
|||
resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020"
|
||||
integrity sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==
|
||||
|
||||
xtend@^4.0.0, xtend@~4.0.1:
|
||||
xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
||||
|
|
Loading…
Reference in a new issue