diff --git a/libs/designer-ui/src/lib/html/plugins/toolbar/helper/util.ts b/libs/designer-ui/src/lib/html/plugins/toolbar/helper/util.ts index 6d9d7b8ce01..a787b64e21a 100644 --- a/libs/designer-ui/src/lib/html/plugins/toolbar/helper/util.ts +++ b/libs/designer-ui/src/lib/html/plugins/toolbar/helper/util.ts @@ -1,5 +1,6 @@ import { encodeStringSegmentTokensInDomContext } from '../../../../editor/base/utils/parsesegments'; import type { ValueSegment } from '@microsoft/logic-apps-shared'; +import DomPurify from 'dompurify'; const htmlUnsafeCharacters = ['<', '>']; const htmlUnsafeCharacterEncodingMap: Record = htmlUnsafeCharacters.reduce( @@ -95,12 +96,17 @@ export const encodeOrDecodeSegmentValue = (value: string, encodingMap: Record): HTMLElement => { - const encodedHtmlEditorString = encodeStringSegmentTokensInDomContext(htmlEditorString, nodeMap); + // Comments at the start of a DOM are lost when parsing HTML strings, so we wrap the HTML string in a
. + const wrappedHtmlEditorString = `
${htmlEditorString}
`; - const tempElement = document.createElement('div'); + const purifiedHtmlEditorString = DomPurify.sanitize(wrappedHtmlEditorString, { ADD_TAGS: ['#comment'] }); + const encodedHtmlEditorString = encodeStringSegmentTokensInDomContext(purifiedHtmlEditorString, nodeMap); + + const tempElement = document.createElement('div', {}); tempElement.innerHTML = encodedHtmlEditorString; - return tempElement; + // Unwrap the wrapper
. + return tempElement.children[0] as HTMLElement; }; export const isAttributeSupportedByHtmlEditor = (tagName: string, attribute: string): boolean => { diff --git a/package-lock.json b/package-lock.json index f9b08a0c782..292b6822aed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,6 +44,7 @@ "consola": "2.15.3", "core-js": "3.24.1", "d3-shape": "3.1.0", + "dompurify": "^3.0.11", "elkjs": "^0.8.2", "fuse.js": "6.6.2", "github-markdown-css": "5.1.0", @@ -123,6 +124,7 @@ "@testing-library/react": "13.4.0", "@types/adm-zip": "^0.5.1", "@types/d3-shape": "3.1.1", + "@types/dompurify": "^3.0.5", "@types/jest": "^28.1.8", "@types/js-yaml": "4.0.5", "@types/lodash.frompairs": "^4.0.1", @@ -9075,6 +9077,15 @@ "@types/ms": "*" } }, + "node_modules/@types/dompurify": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", + "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", + "dev": true, + "dependencies": { + "@types/trusted-types": "*" + } + }, "node_modules/@types/eslint": { "version": "8.4.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", @@ -9609,6 +9620,12 @@ "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==" }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true + }, "node_modules/@types/tunnel": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", @@ -14052,6 +14069,11 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.11.tgz", + "integrity": "sha512-Fan4uMuyB26gFV3ovPoEoQbxRRPfTu3CvImyZnhGq5fsIEO+gEFLp45ISFt+kQBWsK5ulDdT0oV28jS1UrwQLg==" + }, "node_modules/domutils": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", @@ -37290,6 +37312,15 @@ "@types/ms": "*" } }, + "@types/dompurify": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", + "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", + "dev": true, + "requires": { + "@types/trusted-types": "*" + } + }, "@types/eslint": { "version": "8.4.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", @@ -37807,6 +37838,12 @@ "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==" }, + "@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true + }, "@types/tunnel": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", @@ -41126,6 +41163,11 @@ "domelementtype": "^2.2.0" } }, + "dompurify": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.11.tgz", + "integrity": "sha512-Fan4uMuyB26gFV3ovPoEoQbxRRPfTu3CvImyZnhGq5fsIEO+gEFLp45ISFt+kQBWsK5ulDdT0oV28jS1UrwQLg==" + }, "domutils": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", diff --git a/package.json b/package.json index c0d69c3fe43..74b8af88ca6 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "consola": "2.15.3", "core-js": "3.24.1", "d3-shape": "3.1.0", + "dompurify": "^3.0.11", "elkjs": "^0.8.2", "fuse.js": "6.6.2", "github-markdown-css": "5.1.0", @@ -153,6 +154,7 @@ "@testing-library/react": "13.4.0", "@types/adm-zip": "^0.5.1", "@types/d3-shape": "3.1.1", + "@types/dompurify": "^3.0.5", "@types/jest": "^28.1.8", "@types/js-yaml": "4.0.5", "@types/lodash.frompairs": "^4.0.1",