From 00864bd31236b48c9ce97b74cb8f11165392cd99 Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Tue, 27 Feb 2024 17:42:16 +0100 Subject: [PATCH 1/4] CB-4743 adds html sanitizer --- webapp/packages/core-utils/package.json | 2 ++ webapp/packages/core-utils/src/index.ts | 1 + .../core-utils/src/sanitizeHtml.test.tsx | 35 +++++++++++++++++++ .../packages/core-utils/src/sanitizeHtml.ts | 14 ++++++++ webapp/yarn.lock | 14 +++++++- 5 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 webapp/packages/core-utils/src/sanitizeHtml.test.tsx create mode 100644 webapp/packages/core-utils/src/sanitizeHtml.ts diff --git a/webapp/packages/core-utils/package.json b/webapp/packages/core-utils/package.json index cb6c9e7b07..bb0846e29c 100644 --- a/webapp/packages/core-utils/package.json +++ b/webapp/packages/core-utils/package.json @@ -21,6 +21,7 @@ "@timohausmann/quadtree-ts": "2.0.0-beta.1", "@types/whatwg-mimetype": "^3.0.2", "clsx": "^2.0.0", + "dompurify": "^3.0.9", "fast-deep-equal": "^3.1.3", "md5": "^2.3.0", "mobx": "^6.12.0", @@ -32,6 +33,7 @@ }, "peerDependencies": {}, "devDependencies": { + "@types/dompurify": "^3.0.5", "@types/jest": "^29.5.10", "@types/md5": "~2.3.5", "@types/underscore": "^1.11.15", diff --git a/webapp/packages/core-utils/src/index.ts b/webapp/packages/core-utils/src/index.ts index e5a850255b..685cd477c3 100644 --- a/webapp/packages/core-utils/src/index.ts +++ b/webapp/packages/core-utils/src/index.ts @@ -76,3 +76,4 @@ export * from './removeLineBreak'; export * from './replaceSubstring'; export * from './formatNumber'; export * from './withTimestamp'; +export * from './sanitizeHtml'; diff --git a/webapp/packages/core-utils/src/sanitizeHtml.test.tsx b/webapp/packages/core-utils/src/sanitizeHtml.test.tsx new file mode 100644 index 0000000000..73172a9c9a --- /dev/null +++ b/webapp/packages/core-utils/src/sanitizeHtml.test.tsx @@ -0,0 +1,35 @@ +import { sanitizeHtml } from './sanitizeHtml'; + +describe('sanitize', () => { + it('should sanitize input', () => { + const input = ''; + const output = sanitizeHtml(input); + expect(output).toBe(''); + }); + + it('should sanitize input and keep safe tags', () => { + const input = '
qwe
asd
'; + const output = sanitizeHtml(input); + expect(output).toBe('
qwe
asd
'); + }); + + it('should not sanitize safe input', () => { + const input = 'Hello, world!'; + const output = sanitizeHtml(input); + expect(output).toBe(input); + }); + + it('should sanitize unsafe input', () => { + const input = ''; + const output = sanitizeHtml(input); + expect(output).toBe(''); + }); + + it('should sanitize unsafe input with attributes', () => { + const input = 'click me'; + const output = sanitizeHtml(input); + expect(output).toBe('click me'); + }); + + it('should sanitize unsafe input with attributes', () => {}); +}); diff --git a/webapp/packages/core-utils/src/sanitizeHtml.ts b/webapp/packages/core-utils/src/sanitizeHtml.ts new file mode 100644 index 0000000000..3412ac349b --- /dev/null +++ b/webapp/packages/core-utils/src/sanitizeHtml.ts @@ -0,0 +1,14 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +import DOMPurify from 'dompurify'; + +export function sanitizeHtml(dirty: T): T extends string ? string : HTMLElement { + const purify = DOMPurify(window); + + return purify.sanitize(dirty, {}) as T extends string ? string : HTMLElement; +} diff --git a/webapp/yarn.lock b/webapp/yarn.lock index b989a7ca8b..8a236e26c7 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -4547,6 +4547,13 @@ dependencies: "@types/ms" "*" +"@types/dompurify@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-3.0.5.tgz#02069a2fcb89a163bacf1a788f73cb415dd75cb7" + integrity sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg== + dependencies: + "@types/trusted-types" "*" + "@types/eslint-scope@^3.7.3": version "3.7.7" resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" @@ -4917,7 +4924,7 @@ resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== -"@types/trusted-types@^2.0.2": +"@types/trusted-types@*", "@types/trusted-types@^2.0.2": version "2.0.7" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== @@ -8287,6 +8294,11 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: dependencies: domelementtype "^2.2.0" +dompurify@^3.0.9: + version "3.0.9" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.0.9.tgz#b3f362f24b99f53498c75d43ecbd784b0b3ad65e" + integrity sha512-uyb4NDIvQ3hRn6NiC+SIFaP4mJ/MdXlvtunaqK9Bn6dD3RuB/1S/gasEjDHD8eiaqdSael2vBv+hOs7Y+jhYOQ== + domutils@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" From 16dde3e89cd8d382314ac346bb0352f89c9763b7 Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Tue, 27 Feb 2024 17:43:41 +0100 Subject: [PATCH 2/4] CB-4743 adds license to sanitizeHtml --- webapp/packages/core-utils/src/sanitizeHtml.test.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/webapp/packages/core-utils/src/sanitizeHtml.test.tsx b/webapp/packages/core-utils/src/sanitizeHtml.test.tsx index 73172a9c9a..b2e2d230cd 100644 --- a/webapp/packages/core-utils/src/sanitizeHtml.test.tsx +++ b/webapp/packages/core-utils/src/sanitizeHtml.test.tsx @@ -1,3 +1,10 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ import { sanitizeHtml } from './sanitizeHtml'; describe('sanitize', () => { From 02e19ceb3f23187ded45e2b6f71fea20a3f62582 Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Tue, 27 Feb 2024 19:14:42 +0100 Subject: [PATCH 3/4] CB-4743 do not use sanitize to purify the json line data --- webapp/packages/core-utils/package.json | 2 -- webapp/packages/core-utils/src/index.ts | 2 +- .../packages/core-utils/src/sanitizeHtml.ts | 14 ------------ ...tml.test.tsx => toSafeHtmlString.test.tsx} | 22 +++++++++---------- .../core-utils/src/toSafeHtmlString.ts | 14 ++++++++++++ webapp/yarn.lock | 14 +----------- 6 files changed, 26 insertions(+), 42 deletions(-) delete mode 100644 webapp/packages/core-utils/src/sanitizeHtml.ts rename webapp/packages/core-utils/src/{sanitizeHtml.test.tsx => toSafeHtmlString.test.tsx} (57%) create mode 100644 webapp/packages/core-utils/src/toSafeHtmlString.ts diff --git a/webapp/packages/core-utils/package.json b/webapp/packages/core-utils/package.json index bb0846e29c..cb6c9e7b07 100644 --- a/webapp/packages/core-utils/package.json +++ b/webapp/packages/core-utils/package.json @@ -21,7 +21,6 @@ "@timohausmann/quadtree-ts": "2.0.0-beta.1", "@types/whatwg-mimetype": "^3.0.2", "clsx": "^2.0.0", - "dompurify": "^3.0.9", "fast-deep-equal": "^3.1.3", "md5": "^2.3.0", "mobx": "^6.12.0", @@ -33,7 +32,6 @@ }, "peerDependencies": {}, "devDependencies": { - "@types/dompurify": "^3.0.5", "@types/jest": "^29.5.10", "@types/md5": "~2.3.5", "@types/underscore": "^1.11.15", diff --git a/webapp/packages/core-utils/src/index.ts b/webapp/packages/core-utils/src/index.ts index 685cd477c3..dbdb6f76fe 100644 --- a/webapp/packages/core-utils/src/index.ts +++ b/webapp/packages/core-utils/src/index.ts @@ -76,4 +76,4 @@ export * from './removeLineBreak'; export * from './replaceSubstring'; export * from './formatNumber'; export * from './withTimestamp'; -export * from './sanitizeHtml'; +export * from './toSafeHtmlString'; diff --git a/webapp/packages/core-utils/src/sanitizeHtml.ts b/webapp/packages/core-utils/src/sanitizeHtml.ts deleted file mode 100644 index 3412ac349b..0000000000 --- a/webapp/packages/core-utils/src/sanitizeHtml.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2024 DBeaver Corp and others - * - * Licensed under the Apache License, Version 2.0. - * you may not use this file except in compliance with the License. - */ -import DOMPurify from 'dompurify'; - -export function sanitizeHtml(dirty: T): T extends string ? string : HTMLElement { - const purify = DOMPurify(window); - - return purify.sanitize(dirty, {}) as T extends string ? string : HTMLElement; -} diff --git a/webapp/packages/core-utils/src/sanitizeHtml.test.tsx b/webapp/packages/core-utils/src/toSafeHtmlString.test.tsx similarity index 57% rename from webapp/packages/core-utils/src/sanitizeHtml.test.tsx rename to webapp/packages/core-utils/src/toSafeHtmlString.test.tsx index b2e2d230cd..83fd8f5a5e 100644 --- a/webapp/packages/core-utils/src/sanitizeHtml.test.tsx +++ b/webapp/packages/core-utils/src/toSafeHtmlString.test.tsx @@ -5,38 +5,36 @@ * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. */ -import { sanitizeHtml } from './sanitizeHtml'; +import { toSafeHtmlString } from './toSafeHtmlString'; describe('sanitize', () => { it('should sanitize input', () => { const input = ''; - const output = sanitizeHtml(input); - expect(output).toBe(''); + const output = toSafeHtmlString(input); + expect(output).toBe('<script>alert("some unsafe action")</script>'); }); it('should sanitize input and keep safe tags', () => { const input = '
qwe
asd
'; - const output = sanitizeHtml(input); - expect(output).toBe('
qwe
asd
'); + const output = toSafeHtmlString(input); + expect(output).toBe('<div>qwe</div><script>alert("some unsafe action")</script><div>asd</div>'); }); it('should not sanitize safe input', () => { const input = 'Hello, world!'; - const output = sanitizeHtml(input); + const output = toSafeHtmlString(input); expect(output).toBe(input); }); it('should sanitize unsafe input', () => { const input = ''; - const output = sanitizeHtml(input); - expect(output).toBe(''); + const output = toSafeHtmlString(input); + expect(output).toBe('<img src="x" onerror="alert(1)">'); }); it('should sanitize unsafe input with attributes', () => { const input = 'click me'; - const output = sanitizeHtml(input); - expect(output).toBe('click me'); + const output = toSafeHtmlString(input); + expect(output).toBe('<a href="javascript:alert(1)">click me</a>'); }); - - it('should sanitize unsafe input with attributes', () => {}); }); diff --git a/webapp/packages/core-utils/src/toSafeHtmlString.ts b/webapp/packages/core-utils/src/toSafeHtmlString.ts new file mode 100644 index 0000000000..783869079c --- /dev/null +++ b/webapp/packages/core-utils/src/toSafeHtmlString.ts @@ -0,0 +1,14 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +export function toSafeHtmlString(dirty: string): string { + const el = document.createElement('div'); + el.innerText = el.textContent = dirty; + dirty = el.innerHTML; + + return dirty; +} diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 8a236e26c7..b989a7ca8b 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -4547,13 +4547,6 @@ dependencies: "@types/ms" "*" -"@types/dompurify@^3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-3.0.5.tgz#02069a2fcb89a163bacf1a788f73cb415dd75cb7" - integrity sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg== - dependencies: - "@types/trusted-types" "*" - "@types/eslint-scope@^3.7.3": version "3.7.7" resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" @@ -4924,7 +4917,7 @@ resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== -"@types/trusted-types@*", "@types/trusted-types@^2.0.2": +"@types/trusted-types@^2.0.2": version "2.0.7" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== @@ -8294,11 +8287,6 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: dependencies: domelementtype "^2.2.0" -dompurify@^3.0.9: - version "3.0.9" - resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.0.9.tgz#b3f362f24b99f53498c75d43ecbd784b0b3ad65e" - integrity sha512-uyb4NDIvQ3hRn6NiC+SIFaP4mJ/MdXlvtunaqK9Bn6dD3RuB/1S/gasEjDHD8eiaqdSael2vBv+hOs7Y+jhYOQ== - domutils@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" From 7e6d93797bccabac870c352a181f3a4376ca7dfe Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Tue, 27 Feb 2024 20:00:43 +0100 Subject: [PATCH 4/4] CB-4743 fix: toSafeHtmlString test correct cases --- .../core-utils/src/toSafeHtmlString.test.tsx | 26 +++++-------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/webapp/packages/core-utils/src/toSafeHtmlString.test.tsx b/webapp/packages/core-utils/src/toSafeHtmlString.test.tsx index 83fd8f5a5e..147716b8e2 100644 --- a/webapp/packages/core-utils/src/toSafeHtmlString.test.tsx +++ b/webapp/packages/core-utils/src/toSafeHtmlString.test.tsx @@ -7,34 +7,22 @@ */ import { toSafeHtmlString } from './toSafeHtmlString'; -describe('sanitize', () => { - it('should sanitize input', () => { +describe('toSafeHtmlString', () => { + it('should make html string safe', () => { const input = ''; const output = toSafeHtmlString(input); expect(output).toBe('<script>alert("some unsafe action")</script>'); }); - it('should sanitize input and keep safe tags', () => { - const input = '
qwe
asd
'; + it('should return empty string', () => { + const input = ''; const output = toSafeHtmlString(input); - expect(output).toBe('<div>qwe</div><script>alert("some unsafe action")</script><div>asd</div>'); + expect(output).toBe(''); }); - it('should not sanitize safe input', () => { - const input = 'Hello, world!'; + it('should return the same string', () => { + const input = 'some safe string'; const output = toSafeHtmlString(input); expect(output).toBe(input); }); - - it('should sanitize unsafe input', () => { - const input = ''; - const output = toSafeHtmlString(input); - expect(output).toBe('<img src="x" onerror="alert(1)">'); - }); - - it('should sanitize unsafe input with attributes', () => { - const input = 'click me'; - const output = toSafeHtmlString(input); - expect(output).toBe('<a href="javascript:alert(1)">click me</a>'); - }); });