fix(security-markdown-widget): allow sanitization of preview content (#4886)

This commit is contained in:
Erez Rokah 2021-01-29 06:50:48 -08:00 committed by GitHub
parent da31332641
commit 27aec85550
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 83 additions and 4 deletions

View File

@ -22,6 +22,7 @@
"build:esm": "cross-env NODE_ENV=esm babel src --out-dir dist/esm --ignore \"**/__tests__\" --root-mode upward"
},
"dependencies": {
"dompurify": "^2.2.6",
"is-hotkey": "^0.2.0",
"mdast-util-definitions": "^1.2.3",
"mdast-util-to-string": "^1.0.5",

View File

@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { WidgetPreviewContainer } from 'netlify-cms-ui-default';
import { markdownToHtml } from './serializers';
import DOMPurify from 'dompurify';
class MarkdownPreview extends React.Component {
static propTypes = {
getAsset: PropTypes.func.isRequired,
@ -11,14 +11,15 @@ class MarkdownPreview extends React.Component {
};
render() {
const { value, getAsset, resolveWidget } = this.props;
const { value, getAsset, resolveWidget, field } = this.props;
if (value === null) {
return null;
}
const html = markdownToHtml(value, { getAsset, resolveWidget });
const toRender = field?.get('sanitize_preview', false) ? DOMPurify.sanitize(html) : html;
return <WidgetPreviewContainer dangerouslySetInnerHTML={{ __html: html }} />;
return <WidgetPreviewContainer dangerouslySetInnerHTML={{ __html: toRender }} />;
}
}

View File

@ -15,6 +15,36 @@ exports[`Markdown Preview renderer HTML rendering should render HTML 1`] = `
/>
`;
exports[`Markdown Preview renderer HTML sanitization should not sanitize HTML 1`] = `
.emotion-0 {
margin: 15px 2px;
}
<div
className="emotion-0 emotion-1"
dangerouslySetInnerHTML={
Object {
"__html": "<img src=\\"foobar.png\\" onerror=\\"alert('hello')\\">",
}
}
/>
`;
exports[`Markdown Preview renderer HTML sanitization should sanitize HTML 1`] = `
.emotion-0 {
margin: 15px 2px;
}
<div
className="emotion-0 emotion-1"
dangerouslySetInnerHTML={
Object {
"__html": "<img src=\\"foobar.png\\">",
}
}
/>
`;
exports[`Markdown Preview renderer Markdown rendering Code should render code 1`] = `
.emotion-0 {
margin: 15px 2px;

View File

@ -1,6 +1,7 @@
import React from 'react';
import { create, act } from 'react-test-renderer';
import { padStart } from 'lodash';
import { Map } from 'immutable';
import MarkdownPreview from '../MarkdownPreview';
import { markdownToHtml } from '../serializers';
@ -216,4 +217,44 @@ I get 10 times more traffic from [Google] than from [Yahoo] or [MSN].
expect(root.toJSON()).toMatchSnapshot();
});
});
describe('HTML sanitization', () => {
it('should sanitize HTML', async () => {
const value = `<img src="foobar.png" onerror="alert('hello')">`;
const field = Map({ sanitize_preview: true });
let root;
await act(async () => {
root = create(
<MarkdownPreview
value={value}
getAsset={jest.fn()}
resolveWidget={jest.fn()}
field={field}
/>,
);
});
expect(root.toJSON()).toMatchSnapshot();
});
it('should not sanitize HTML', async () => {
const value = `<img src="foobar.png" onerror="alert('hello')">`;
const field = Map({ sanitize_preview: false });
let root;
await act(async () => {
root = create(
<MarkdownPreview
value={value}
getAsset={jest.fn()}
resolveWidget={jest.fn()}
field={field}
/>,
);
});
expect(root.toJSON()).toMatchSnapshot();
});
});
});

View File

@ -16,6 +16,7 @@ The markdown widget provides a full fledged text editor allowing users to format
* `buttons`: an array of strings representing the formatting buttons to display (all shown by default). Buttons include: `bold`, `italic`, `code`, `link`, `heading-one`, `heading-two`, `heading-three`, `heading-four`, `heading-five`, `heading-six`, `quote`, `bulleted-list`, and `numbered-list`.
* `editor_components`: an array of strings representing the names of editor components to display (all shown by default). Netlify CMS includes `image` and `code-block` editor components by default, and custom components may be [created and registered](/docs/custom-widgets/#registereditorcomponent).
* `modes`: an array of strings representing the names of allowed editor modes. Possible modes are `raw` and `rich_text`. A toggle button appears in the toolbar when more than one mode is available.
* `sanitize_preview`: accepts a boolean value, `false` by default. Sanitizes markdown preview to prevent XSS attacks - might alter the preview content.
* **Example:**
```yaml

View File

@ -7313,6 +7313,11 @@ domhandler@^2.3.0:
dependencies:
domelementtype "1"
dompurify@^2.2.6:
version "2.2.6"
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.2.6.tgz#54945dc5c0b45ce5ae228705777e8e59d7b2edc4"
integrity sha512-7b7ZArhhH0SP6W2R9cqK6RjaU82FZ2UPM7RO8qN1b1wyvC/NY1FNWcX1Pu00fFOAnzEORtwXe4bPaClg6pUybQ==
domutils@^1.5.1, domutils@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"