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.