diff --git a/README.md b/README.md index 01178256d..8b8b934db 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ - [Browser Support](#-browser-support) - [Pull Request Steps](#-pull-request-steps) - [Contributing](#-contributing) +- [Dependencies](#-dependencies) - [TOAST UI Family](#-toast-ui-family) - [Used By](#-used-by) - [License](#-license) @@ -184,6 +185,10 @@ For more information on PR's steps, please see links in the Contributing section - [Commit convention](https://github.com/nhn/tui.grid/blob/master/docs/COMMIT_MESSAGE_CONVENTION.md) - [Issue guideline](https://github.com/nhn/tui.grid/tree/master/.github/ISSUE_TEMPLATE) +## 🔩 Dependencies + +* [DOMPurify](https://github.com/cure53/DOMPurify) + ## 🍞 TOAST UI Family - [TOAST UI Calendar](https://github.com/nhn/tui.calendar) diff --git a/packages/toast-ui.grid/cypress/integration/renderer.spec.ts b/packages/toast-ui.grid/cypress/integration/renderer.spec.ts index febc51299..96906c6a6 100644 --- a/packages/toast-ui.grid/cypress/integration/renderer.spec.ts +++ b/packages/toast-ui.grid/cypress/integration/renderer.spec.ts @@ -72,3 +72,16 @@ it('should apply the options to default renderer', () => { .should('have.attr', 'myCustom', 'my-custom') .should('have.attr', 'title', 'my Lee'); }); + +it('should render data to plain text in default renderer', () => { + const data = [{ tag: '' }]; + const columns = [ + { + name: 'tag', + }, + ]; + + cy.createGrid({ data, columns }); + + cy.getByCls('cell-content').invoke('html').should('to.eq', ''); +}); diff --git a/packages/toast-ui.grid/package-lock.json b/packages/toast-ui.grid/package-lock.json index 74dd7d856..ccceadb7c 100644 --- a/packages/toast-ui.grid/package-lock.json +++ b/packages/toast-ui.grid/package-lock.json @@ -6125,6 +6125,15 @@ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, + "@types/dompurify": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.3.3.tgz", + "integrity": "sha512-nnVQSgRVuZ/843oAfhA25eRSNzUFcBPk/LOiw5gm8mD9/X7CNcbRkQu/OsjCewO8+VIYfPxUnXvPEVGenw14+w==", + "dev": true, + "requires": { + "@types/trusted-types": "*" + } + }, "@types/eslint-visitor-keys": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", @@ -6272,6 +6281,12 @@ "integrity": "sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA==", "dev": true }, + "@types/trusted-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", + "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==", + "dev": true + }, "@types/uglify-js": { "version": "3.9.2", "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.9.2.tgz", @@ -10538,6 +10553,11 @@ "domelementtype": "1" } }, + "dompurify": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.9.tgz", + "integrity": "sha512-3zOnuTwup4lPV/GfGS6UzG4ub9nhSYagR/5tB3AvDEwqyy5dtyCM2dVjwGDCnrPerXifBKTYh/UWCGKK7ydhhw==" + }, "domutils": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", diff --git a/packages/toast-ui.grid/package.json b/packages/toast-ui.grid/package.json index 55ab343e0..d6e1136ea 100644 --- a/packages/toast-ui.grid/package.json +++ b/packages/toast-ui.grid/package.json @@ -50,6 +50,7 @@ "@storybook/addon-notes": "^5.3.19", "@storybook/html": "^5.3.19", "@toast-ui/select-box": "^1.0.0", + "@types/dompurify": "^2.3.3", "@types/node": "^12.0.0", "@types/webpack-env": "^1.13.8", "@typescript-eslint/eslint-plugin": "^2.9.0", @@ -81,6 +82,7 @@ "webpack-merge": "^4.2.1" }, "dependencies": { + "dompurify": "^2.3.9", "tui-date-picker": "^4.1.0", "tui-pagination": "^3.4.0", "xlsx": "^0.17.1" diff --git a/packages/toast-ui.grid/src/renderer/default.ts b/packages/toast-ui.grid/src/renderer/default.ts index 965d4b231..e5510cb34 100644 --- a/packages/toast-ui.grid/src/renderer/default.ts +++ b/packages/toast-ui.grid/src/renderer/default.ts @@ -1,6 +1,7 @@ import { CellRenderer, CellRendererProps } from '@t/renderer'; import { cls } from '../helper/dom'; import { isFunction } from '../helper/common'; +import { sanitize } from 'dompurify'; type IfEquals = (() => T extends X ? 0 : 1) extends () => T extends Y ? 0 @@ -69,6 +70,6 @@ export class DefaultRenderer implements CellRenderer { } public render(props: CellRendererProps) { - this.el.innerHTML = `${props.formattedValue}`; + this.el.innerHTML = sanitize(`${props.formattedValue}`); } } diff --git a/packages/toast-ui.grid/src/view/clipboard.tsx b/packages/toast-ui.grid/src/view/clipboard.tsx index 73d8ce470..cc1f06375 100644 --- a/packages/toast-ui.grid/src/view/clipboard.tsx +++ b/packages/toast-ui.grid/src/view/clipboard.tsx @@ -15,6 +15,7 @@ import { getText } from '../query/clipboard'; import { convertTextToData } from '../helper/common'; import GridEvent from '../event/gridEvent'; import { getEventBus, EventBus } from '../event/eventBus'; +import { sanitize } from 'dompurify'; interface StoreProps { navigating: boolean; @@ -111,7 +112,8 @@ class ClipboardComp extends Component { } const { el } = this; - const html = clipboardData.getData('text/html'); + const html = sanitize(clipboardData.getData('text/html')); + let data; if (html && html.indexOf('table') !== -1) { // step 1: Append copied data on contenteditable element to parsing correctly table data.