From 86508d5d9504523d9c99af6898b22418b239f19b Mon Sep 17 00:00:00 2001 From: Remigijus Kiminas Date: Mon, 4 May 2026 12:22:47 +0000 Subject: [PATCH 1/5] CSP based on ideas from #1123 --- __tests__/options/trusted-type-policy.test.ts | 25 +++++++++++++++++++ examples/webpack/src/index.js | 8 +++++- src/index.ts | 7 +++++- src/types.ts | 6 +++++ 4 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 __tests__/options/trusted-type-policy.test.ts diff --git a/__tests__/options/trusted-type-policy.test.ts b/__tests__/options/trusted-type-policy.test.ts new file mode 100644 index 00000000..119140e0 --- /dev/null +++ b/__tests__/options/trusted-type-policy.test.ts @@ -0,0 +1,25 @@ +import htmlToDOM from 'html-dom-parser'; + +import parse from '../../src'; + +vi.mock('html-dom-parser', () => ({ + default: vi.fn(() => []), +})); + +describe('trustedTypePolicy option', () => { + it('passes trustedTypePolicy to html-dom-parser', () => { + const trustedTypePolicy = { + createHTML: vi.fn((input: string) => input), + }; + + parse('
test
', { trustedTypePolicy }); + + expect(htmlToDOM).toHaveBeenCalledWith( + '
test
', + expect.objectContaining({ + lowerCaseAttributeNames: false, + trustedTypePolicy, + }), + ); + }); +}); diff --git a/examples/webpack/src/index.js b/examples/webpack/src/index.js index ace2d1dc..b00a491d 100644 --- a/examples/webpack/src/index.js +++ b/examples/webpack/src/index.js @@ -3,4 +3,10 @@ import parse from 'html-react-parser'; const root = createRoot(document.getElementById('root')); -root.render(parse('

HTMLReactParser loaded with Webpack

')); +let trustedHtml = (window.trustedTypes && window.trustedTypes.createPolicy) + ? window.trustedTypes.createPolicy('csp-react-html', {createHTML: function(s) { return s; }}) + : null; + +root.render(parse('

HTMLReactParser loaded with Webpack

',{ + trustedTypePolicy : trustedHtml +})); diff --git a/src/index.ts b/src/index.ts index a72bab8f..c1401d9e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -31,8 +31,13 @@ export default function HTMLReactParser( return []; } + const htmlToDOMOptions = { + ...(options?.htmlparser2 ?? domParserOptions), + trustedTypePolicy: options?.trustedTypePolicy, + }; + return domToReact( - htmlToDOM(html, options?.htmlparser2 ?? domParserOptions), + htmlToDOM(html, htmlToDOMOptions), options, ); } diff --git a/src/types.ts b/src/types.ts index f1f91e9e..e21313f1 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,9 +3,15 @@ import type { DOMNode } from 'html-dom-parser'; import type { ParserOptions } from 'htmlparser2'; import type { JSX, ReactNode } from 'react'; +interface TrustedTypePolicyLike { + createHTML(input: string): unknown; +} + export interface HTMLReactParserOptions { htmlparser2?: ParserOptions & DomHandlerOptions; + trustedTypePolicy?: TrustedTypePolicyLike; + library?: { /* eslint-disable @typescript-eslint/no-explicit-any */ cloneElement: ( From 806bbfc15d42dee3f94fc01c23ef77868cc88d5d Mon Sep 17 00:00:00 2001 From: Remigijus Kiminas Date: Mon, 4 May 2026 21:13:53 +0000 Subject: [PATCH 2/5] Use types from html-dom-parser --- src/types.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/types.ts b/src/types.ts index e21313f1..7055b2c3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,16 +1,11 @@ import type { DomHandlerOptions } from 'domhandler'; -import type { DOMNode } from 'html-dom-parser'; +import type { DOMNode, TrustedTypePolicy } from 'html-dom-parser'; import type { ParserOptions } from 'htmlparser2'; import type { JSX, ReactNode } from 'react'; -interface TrustedTypePolicyLike { - createHTML(input: string): unknown; -} - export interface HTMLReactParserOptions { htmlparser2?: ParserOptions & DomHandlerOptions; - - trustedTypePolicy?: TrustedTypePolicyLike; + trustedTypePolicy?: TrustedTypePolicy; library?: { /* eslint-disable @typescript-eslint/no-explicit-any */ From 5acded90b1972b93bdd64a01cd6f0036b7b34138 Mon Sep 17 00:00:00 2001 From: Remigijus Kiminas Date: Tue, 5 May 2026 05:12:47 +0000 Subject: [PATCH 3/5] Lint fixes --- __tests__/options/trusted-type-policy.test.ts | 50 +++++++++---------- src/index.ts | 5 +- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/__tests__/options/trusted-type-policy.test.ts b/__tests__/options/trusted-type-policy.test.ts index 119140e0..ec9be128 100644 --- a/__tests__/options/trusted-type-policy.test.ts +++ b/__tests__/options/trusted-type-policy.test.ts @@ -1,25 +1,25 @@ -import htmlToDOM from 'html-dom-parser'; - -import parse from '../../src'; - -vi.mock('html-dom-parser', () => ({ - default: vi.fn(() => []), -})); - -describe('trustedTypePolicy option', () => { - it('passes trustedTypePolicy to html-dom-parser', () => { - const trustedTypePolicy = { - createHTML: vi.fn((input: string) => input), - }; - - parse('
test
', { trustedTypePolicy }); - - expect(htmlToDOM).toHaveBeenCalledWith( - '
test
', - expect.objectContaining({ - lowerCaseAttributeNames: false, - trustedTypePolicy, - }), - ); - }); -}); +import htmlToDOM from 'html-dom-parser'; + +import parse from '../../src'; + +vi.mock('html-dom-parser', () => ({ + default: vi.fn(() => []), +})); + +describe('trustedTypePolicy option', () => { + it('passes trustedTypePolicy to html-dom-parser', () => { + const trustedTypePolicy = { + createHTML: vi.fn((input: string) => input), + }; + + parse('
test
', { trustedTypePolicy }); + + expect(htmlToDOM).toHaveBeenCalledWith( + '
test
', + expect.objectContaining({ + lowerCaseAttributeNames: false, + trustedTypePolicy, + }), + ); + }); +}); diff --git a/src/index.ts b/src/index.ts index c1401d9e..6ca7f508 100644 --- a/src/index.ts +++ b/src/index.ts @@ -36,8 +36,5 @@ export default function HTMLReactParser( trustedTypePolicy: options?.trustedTypePolicy, }; - return domToReact( - htmlToDOM(html, htmlToDOMOptions), - options, - ); + return domToReact(htmlToDOM(html, htmlToDOMOptions), options); } From 1b4b553b4506a7c546b84aa8ac1563d1de2f2246 Mon Sep 17 00:00:00 2001 From: Remigijus Kiminas Date: Tue, 5 May 2026 16:57:13 +0000 Subject: [PATCH 4/5] Update dom package --- .gitignore | 4 ++-- package-lock.json | 9 ++++----- package.json | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 46fcbcae..63cc6e39 100644 --- a/.gitignore +++ b/.gitignore @@ -41,8 +41,8 @@ jspm_packages .node_repl_history # Build files -dist/ -lib/ +# dist/ +# lib/ # Vim swap files *.swp diff --git a/package-lock.json b/package-lock.json index 207640ae..72eaafa4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "license": "MIT", "dependencies": { "domhandler": "6.0.1", - "html-dom-parser": "7.0.1", + "html-dom-parser": "7.1.0", "react-property": "2.0.2", "style-to-js": "1.1.21" }, @@ -4677,16 +4677,15 @@ } }, "node_modules/html-dom-parser": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/html-dom-parser/-/html-dom-parser-7.0.1.tgz", - "integrity": "sha512-loRBDTCY/05/jAC63J1X9ID+xjRucmpLkIcQO0IRbOubBo5ucnpUpyXXob9UMXOskMZlu7KPsDP/2KOMelzJNA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/html-dom-parser/-/html-dom-parser-7.1.0.tgz", + "integrity": "sha512-83BgaFSW/Sj6QTotGenvPvKfGxFzpFfrJNYes77mzqnq+YjVm12d4qeG0+108w4ejnam/+nCnnLuyyJlXkuPtA==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/remarkablemark" } ], - "license": "MIT", "dependencies": { "domhandler": "6.0.1", "htmlparser2": "12.0.0" diff --git a/package.json b/package.json index a2017515..20f5626a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ ], "dependencies": { "domhandler": "6.0.1", - "html-dom-parser": "7.0.1", + "html-dom-parser": "7.1.0", "react-property": "2.0.2", "style-to-js": "1.1.21" }, From 88a197fd4a53b9938b6c7889e5eb32eea051dea5 Mon Sep 17 00:00:00 2001 From: Remigijus Kiminas Date: Tue, 5 May 2026 16:59:44 +0000 Subject: [PATCH 5/5] Restore gitignore --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 63cc6e39..46fcbcae 100644 --- a/.gitignore +++ b/.gitignore @@ -41,8 +41,8 @@ jspm_packages .node_repl_history # Build files -# dist/ -# lib/ +dist/ +lib/ # Vim swap files *.swp